What does ActionBar#setDefaultDisplayHomeAsUpEnabled in Android Support library? - android

I know what setDisplayHomeAsUpEnabled does, but what is setDefaultDisplayHomeAsUpEnabled for I can only wonder. No documentation found, cannot find anything except it is being used.

This method is only available in the Support Action Bar, not in the "native" ActionBar class available since Android 3. More importantly, it is annotated with #hide in the source, meaning it is not part of the official API for third-party developers. That is why it is nowhere documented by Google. You should just not use it.
Having a deeper look into the sources, I found the method implemented in WindowDecorActionBar:
public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
if (!mDisplayHomeAsUpSet) {
setDisplayHomeAsUpEnabled(enable);
}
}
So basically it does exactly the same as using setDisplayHomeAsUpEnabled, but only if the value has not yet been set manually using the said function.
tldr: you should always use setDisplayHomeAsUpEnabled and ignore the default method.

Related

Is "TypedArray should be recycled" a false positive in Lint when using try-with-resources?

I have read the dozens of questions here on SO regarding recycling TypedArrays, but I guess they are a bit too old and written before we could widely use try-with-resource statements, so none of them talk about using the AutoCloseable implementation of the TypedArray, which is present since API Level 31
So the question remains: is this a false positive in Lint?
If anything, that warning should be a minSDK warning if applicable, right?
Can we simply write the following since the full try-with support (if we do it after SDK Level >= 31 check)?
try (TypedArray array = getContext().obtainStyledAttributes(attrs) {
// Do someting
}
// End of method
My guess is yes, as this is the AutoCloseable implementation of TypedArray
So the question remains: is this a false positive in Lint?
No, it is not. Because close method in AutoCloseable interface is not magically called when using try/catch.
Instead you have to use use method and then and only then you can get rid of try/catch like following:
getContext().obtainStyledAttributes(attrs).use({
// Do something
});
But, be aware that use method from TypedArray class is available only since Android 31
If you prefer a backwards compatible solution, you can use use method from androidx.core:core-ktx library.
As TypedArray also provides of a use method you will have to take care of adding the following import:
import androidx.core.content.res.use

Implement multiple methods for different API levels and not do if else to decide which one to call

I wonder if it's possible to implement multiple methods to support different API levels and call the right one without if(android.os.Build.VERSION.SDK_INT >= ...) else if...?
I want to use android platform's newer features like streams etc. and still support backwards.
Example:
wrote a method
public void myMethod24() {
// some logic requires api level N(24) and above
}
but my app supports lower apis, so i need another method that's compatible with them.
here is a method compatible for older versions:
public void myMethod21() {
// the same logic, requires api level LOLLIPOP(21) and above
}
How to use the correct method for current running version without doing this ugly if else:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
myMethod24();
} else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
myMethod21();
}
Maybe annotate my methods with #RequiresApi(), #TargetApi or something else..?
i saw this question but the answer there is with if else.
How to use the correct method for current running version without doing this ugly if else
You can't. Somewhere, something needs to do the version check and route to the appropriate logic.
Maybe annotate my methods with #RequiresApi(), #TargetApi or something else..?
Those are there to help advise the compiler that you know what you are doing. They do not code-generate the version comparisons.
Depending on what you are doing, existing ...Compat classes might handle the version checks for you (e.g., NotificationCompat.Builder). If you were using Kotlin, we could come up with some funky code that hides the if checks. And there might be a third-party library that offers some annotation-based code generator that code-generates the if checks.

Android #SuppressWarnings at statement-level, rather than method-level

TL;DR: Can Android's #SuppressWarnings("deprecation"), or similar, be applied to a single statement, rather than an entire method?
I have a method myMethod that uses deprecated method ImageView.setAlpha():
public void myMethod(ImageView icon) { icon.setAlpha(0xFF); }
To avoid use of the deprecated method in Jelly Bean and subsequent releases, whilst providing backward compatibility, method myMethod can be rewritten as follows:
public void myMethod(ImageView icon) {
if (android.os.Build.VERSION.SDK_INT
>= android.os.Build.VERSION_CODES.JELLY_BEAN)
icon.setImageAlpha(0xFF);
else
icon.setAlpha(0xFF);
}
Moreover, command line warnings generated by Gradle/Lint can be suppressed by prepending method myMethod with #SuppressWarnings("deprecation"). But, that suppresses all deprecated warnings generated by method myMethod, rather than just the single warning generated by statement icon.setAlpha(0xFF).
Can I suppress the single deprecation warning generated by statement icon.setAlpha(0xFF), rather than suppressing all deprecation warnings generated by method myMethod?
You can achieve it as follows if you are using Android Studio:
//noinspection deprecation
icon.setAlpha(0xFF);
For your future reference: The correct format can be easily generated in Android-Studio as follows:
Press alt+Enter on the statement which is throwing warning.
Then Expand the option Deprecated API usage options
Click on Suppress for statement
Following Image shows the process:
In your case since you are not using IDE:
Unfortunately there is no direct way to achieve it at method body level. Since you have already moved the deprecated part in individual method and marked it with #SuppressWarnings this should be best you can achieve.
There are some posts which claim to have solved it by using fully qualified class name instead of import. But looks like the issue has been fixed in Java 9. Since current popular java version for android is 8.x this should help in short term. You can refer this SO for more details

Handling dependencies in Library projects

I have one Android open source library project in which I have been using a separate library for logging (Which is also developed by me).
The logging library is not that important, but it makes the development and debugging easier (Both for me and the user of the main library).
I am using gradle (and Jitpack) to use both the libraries. Now the Logging library is actually having few extra permissions in manifest (For writing logs to file, not necessary for the main library).
Now one of the user asked me to remove the extra permissions. And I don't know how can I do that without removing the logging library (or changing the functionality in the logging library itself).
I even realised that few people might not need the logging library at all, so is there a way I can make it optional, like if the user didn't include the Logging library in their build.gradle, it won't get imported which I can detect and not call the logging functions?
I know it sounds confusing, but I'd like to know how to decouple both the libraries. In fact please let me know if you know about any such example from any popular library too.
Yes you can probably do both with some clever tricks.
part 1. PERMISSIONS
About the manifest permissions. It's pretty simple, do not add the permission to the logging library and instead, check it on runtime. Like this:
if (context.checkPermission(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.os.Process.myPid(),
Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
carry on with your write to disk operation ...
}
and then on your documentation you write that IF developers using the library wants to have a local logging, they have to declare WRITE_EXTERNAL_STORAGE on the manifest.
part 2. (not) IMPORTING THE LIB
All I'm gonna write here is by heart and 100% NOT tested. There might (will) be some errors that you'll have to shift around, but hopefully I'll pass a solid idea.
First on the NoobCameraFlash lib you'll define LumberJack in the build.gradle file with provided instead of compile. This will make the compiler known about the LumberJack so compilation can pass, but it won't include it on the actual build.
Then create in your NoobCameraFlash library a class that is a mirror of the library funcionalities. Which means the methods d(String, String), e(String, String) etc.
Then you in this mirror class you will something like the following to check if lumberjack is actually available.
private static boolean lumberJackAvailable = false;
private static boolean lumberJackTested = false;
private static boolean isLumberJackAvailable() {
if(lumberJackTested) return lumberJackAvailable;
lumberJackTested = true;
try {
if(Class.forName("") != null) {
lumberJackAvailable = true;
}
} catch(Throwable e){
// ClassNotFoundException, LinkageError, ExceptionInInitializerError
}
return lumberJackAvailable;
}
public static LumberJackMirror create() {
// could also be a singleton
if(isLumberJackAvailable() return new LumberJackMirror();
else return null;
}
then of course you have to check if(lumberJackMirror != null). So as you can see it's not the most straight forward way of doing things.
Another way that simplifies this a little bit is to create an interface in a different library, that both the mirror and the actual LumberJack implements and use the factory can return an empty implementation of the interface instead of having to null check all the time.
As well, include on the documentation, that if developers want to have the logging functionality they have to add it to the build.gradle. Something like compile 'your_groud_id:lumberjack:version'
edit
another common way of doing that is to make it explicit on the NoobCameraFlash initialisation code. Something like:
NoobCameraFlash.config()
.setLogger(new LumberJack());
so that forces developers to know about LumberJack instead of checking via Class. But that would mean you need some version of LumberJack that is not just static methods.
end_edit
But hopefully just the permission removal will be enough and you don't have to do this part.2 =]
happy coding.

Was PreferenceFragment intentionally excluded from the compatibility package?

I'm looking to write preferences that can be applied to both 3.0 and pre-3.0 devices. Discovering that PreferenceActivity contains deprecated methods (although these are used in the accompanying sample code), I looked at PreferenceFragement and the compatibility package to solve my woes.
It appears, though, that PreferenceFragment isn't in the compatibility package. Can anyone tell me whether this was intentional? If so, can I easily target a range of devices (i.e. < 3.0 and >=3.0) or will I have to jump through hoops? If it wasn't intentionally excluded, can we expect a new release of the compatibility package? Or is there another workaround that is safe to use?
Discovering that PreferenceActivity contains deprecated methods (although these are used in the accompanying sample code)
The deprecated methods are deprecated as of Android 3.0. They are perfectly fine on all versions of Android, but the direction is to use PreferenceFragment on Android 3.0 and higher.
Can anyone tell me whether this was intentional?
My guess is it's a question of engineering time, but that's just a guess.
If so, can I easily target a range of devices (i.e. < 3.0 and >=3.0) or will I have to jump through hoops?
I consider it to be done "easily". Have two separate PreferenceActivity implementations, one using preference headers and PreferenceFragments, the other using the original approach. Choose the right one at the point you need to (e.g., when the user clicks on the options menu item). Here is a sample project demonstrating this. Or, have a single PreferenceActivity that handles both cases, as in this sample project.
If it wasn't intentionally excluded, can we expect a new release of the compatibility package?
You will find out when the rest of us find out, which is to say, if and when it ships.
Or is there another workaround that is safe to use?
See above.
The subtle implication of the answer from #CommonsWare is that - your app must choose between the compatibility API or the built-in fragment API (since SDK 11 or so). In fact that's what the "easily" recommendation has done. In other words, if you want to use PreferenceFragment your app needs to use the built-in fragment API and deal with the deprecated methods on PreferenceActivity. Conversely, if it's important that your app use the compat. API you will be faced with not having a PreferenceFragment class at all. Thus, targeting devices is not a problem, but the hoop-jumping happens when you have to choose one or the other API and thus submit your design to unforeseen workarounds. I need the compat. API so I'm going to create my own PreferenceFragment class and see how that works. In the worst case scenario I'll just create a normal (fragment) layout and bind the view components to the sharedprefs manually...ugh.
EDIT:
After trying and looking at the code at http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java?av=h -- creating my own PreferenceFragment isn't going to happen. It appears the liberal use of package-private in PreferenceManager instead of 'protected' is the main blocker. It really doesn't look like there's any security or really good motivation to have done that and it isn't great for unit-testing but oh well...less typing I guess...
EDIT v2:
Actually it did happen and it worked. It was definitely a headache to make the code work with the Compatibility API JAR. I had to copy about 70% the com.android.preference package from the SDK to my app and then wrestle with typically mediocre-quality Java code in Android. I used v14 of the SDK. It would have been much easier for a Goog engineer to do what I did, contrary to what I've heard some lead Android engineers say about this topic.
BTW - did I say "targeting devices is not a problem"? It totally is...if you use com.android.preference you are not going to be able to swap out with the Compatibility API without major refactoring. Fun log!
Building upon CommonsWare's answer as well as Tenacious' observations, I have come up with a single descendant class solution capable of targeting all current Android API versions with minimal fuss and no code or resource duplication. Please see my answer to the related question over here:
PreferenceActivity Android 4.0 and earlier
or on my blog:
http://www.blackmoonit.com/2012/07/all_api_prefsactivity/
Tested on two tablets running 4.0.3 and 4.0.4 as well as a phone running 4.0.4 and 2.3.3 and also an emulator running 1.6.
See PreferenceFragment-Compat from Machinarius. It was easy to drop in with gradle and I forget that it's even there.
compile 'com.github.machinarius:preferencefragment:0.1.1'
Important Update: The latest revision of the v7 support library now has a native PreferenceFragmentCompat.
On August 2015 Google released the new Preference Support Library v7.
Now you can use the PreferenceFragmentCompat with any Activity or AppCompatActivity
public static class PrefsFragment extends PreferenceFragmentCompat {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
}
You have to set preferenceTheme in your theme:
<style name="AppTheme" parent="#style/Theme.AppCompat.Light">
...
<item name="preferenceTheme">#style/PreferenceThemeOverlay</item>
</style>
In this way you can customize the preferenceTheme to style the layouts used for each preference type without affecting other parts of your Activity.
Tenacious's answer is correct, but here are some more details.
The reason you can't "create a normal layout and bind the view components to the sharedprefs manually" is that there are some surprising omissions in the android.preferences API. PreferenceActivity and PreferenceFragment both have access to critical non-public PreferenceManager methods, without which you can't implement a preference UI of your own.
In particular, to construct a Preference hierarchy from an XML file you need to use a PreferenceManager, but all of PreferenceManager's constructors are either package-private or hidden. The method of attaching the Preference onClick listeners to your activity is also package-private.
And you can't work around this by sneakily putting your implementation in the android.preferences package, because non-public methods in Android APIs are actually omitted from the SDK. With a bit of creativity involving reflection and dynamic proxies, you can still get at them. The only alternative, as Tenacious says, is to fork the entire android.preference package, including at least 15 classes, 5 layouts, and a similar number of style.xml and attrs.xml elements.
So to answer the original question, the reason Google didn't include PreferenceFragment in the compatibility package is that they would have had exactly the same difficulty as Tenacious and myself. Even Google can't go back in time and make those methods public in the old platforms (though I hope they do that in future releases).
My app target is API +14 but due to using support library for some fancy navigation, I couldn't use the android.app.Fragment and had to use android.support.v4.app.Fragment, but I also needed to have PreferenceFragment in place without large changes to code behind.
So my easy fix for having both worlds of support library and PreferenceFragment:
private android.support.v4.app.Fragment fragment;
private android.app.Fragment nativeFragment = null;
private void selectItem(int position) {
fragment = null;
boolean useNativeFragment = false;
switch (position) {
case 0:
fragment = new SampleSupprtFragment1();
break;
case 1:
fragment = new SampleSupprtFragment2();
break;
case 2:
nativeFragment = new SettingsFragment();
useNativeFragment = true;
break;
}
if (useNativeFragment) {
android.app.FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, nativeFragment).commit();
} else {
if (nativeFragment != null) {
getFragmentManager().beginTransaction().remove(nativeFragment)
.commit();
nativeFragment = null;
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment).commit();
}
}
I needed integrate Preferences into application's design and keep support for 2.3 android. So I still needed PreferencesFragment.
After some search I found android-support-v4-preferencefragment lib. This lib save a lot of time for copying and refactoring original PreferencesFragment as Tenacious said. Works fine and users enjoy preferences.

Categories

Resources