IsValidFragment Android API 19

When I try to run my application with Android KitKat, I have an error in PreferenceActivity.

Subclasses of PreferenceActivity must override isValidFragment (String) to ensure that the Fragment class is valid! com.crbin1.labeltodo.ActivityPreference did not check if the fragment com.crbin1.labeltodo.StockPreferenceFragment is valid

In the documentation I will find the following explanation

protected boolean isValidFragment (String filename)

Added to API Level 19

Subclasses must override this method and ensure that the given fragment is a valid type that must be attached to this action. The default implementation returns true for applications built for android: targetSdkVersion older than KITKAT. For later versions, it throws an exception.

I did not find an example to solve the problem.

+49
android android-fragments android-4.4-kitkat
Nov 14 '13 at 8:41
source share
8 answers

Try this ... so we check the correctness of the fragment.

protected boolean isValidFragment(String fragmentName) { return StockPreferenceFragment.class.getName().equals(fragmentName); } 
+65
Nov 22 '13 at 7:57
source share

Out of sheer curiosity, you can also do this:

 @Override protected boolean isValidFragment(String fragmentName) { return MyPreferenceFragmentA.class.getName().equals(fragmentName) || MyPreferenceFragmentB.class.getName().equals(fragmentName) || // ... Finish with your last fragment. ;} 
+24
Dec 10 '13 at
source share

I found that I could grab a copy of my fragment names from my header resource when it was loaded:

 public class MyActivity extends PreferenceActivity { private static List<String> fragments = new ArrayList<String>(); @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.headers,target); fragments.clear(); for (Header header : target) { fragments.add(header.fragment); } } ... @Override protected boolean isValidFragment(String fragmentName) { return fragments.contains(fragmentName); } } 

That way, I need to remember to update the list of fragments encoded in the code if I want to update them.

I was hoping to use getHeaders() and the existing list of headers directly, but it seems that the action is destroyed after onBuildHeaders() and recreated before calling isValidFragment() .

Perhaps this is due to the fact that the Nexus 7 I'm testing on does not actually perform actions with two panels. Hence the need for a static list item.

+20
Jan 21 '15 at 13:43
source share

This API has been added due to a recently discovered vulnerability. See http://ibm.co/1bAA8kF or http://ibm.co/IDm2Es

December 10, 2013 β€œRecently, we discovered a new vulnerability for the Android security group. [...] To be more precise, any application that extended the PreferenceActivity class with exported activity was automatically vulnerable. The patch was provided in Android KitKat. Why your code is broken now, this is due to the Android KitKat patch, which requires applications to override the new PreferenceActivity.isValidFragment method that has been added to the Android Framework. " - From the first link above

+18
Dec 10 '13 at 20:19
source share

Checked using a device with 4.4:

(1) if your proguard.cfg file has this line ( which many define anyway ):

 -keep public class com.fullpackage.MyPreferenceFragment 

(2) than the most efficient implementation:

 @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class EditPreferencesHC extends PreferenceActivity { ... protected boolean isValidFragment (String fragmentName) { return "com.fullpackage.MyPreferenceFragment".equals(fragmentName); } } 
+3
Nov 22 '13 at 23:07 on
source share

I’m not sure that if implemented in the strip there will be no discussed vulnerabilities here , but if it is, then I think the best solution would be to avoid using this static list and just do the following:

  @Override protected boolean isValidFragment(String fragmentName) { ArrayList<Header> target = new ArrayList<>(); loadHeadersFromResource(R.xml.pref_headers, target); for (Header h : target) { if (fragmentName.equals(h.fragment)) return true; } return false; } 
+3
Sep 25 '15 at 14:56
source share

this is my decision:

  • If you need dynamic reflow headers
  • If you use additional functions to start preference activity, the onBuildHeaders () method will fail! (with the additions below to run - why ??? - simply because onBuildHeaders () is never called):

    Intent.putExtra (PreferenceActivity.EXTRA_SHOW_FRAGMEN, Fragment.class.getName ()); Intent.putExtra (PreferenceActivity.EXTRA_NO_HEADERS, true);

This is an example class:

 /** * Preference Header for showing settings and add view as two panels for tablets * for ActionBar we need override onCreate and setContentView */ public class SettingsPreferenceActivity extends PreferenceActivity { /** valid fragment list declaration */ private List<String> validFragmentList; /** some example irrelevant class for holding user session */ SessionManager _sessionManager; @Override public void onBuildHeaders(List<Header> target) { /** load header from res */ loadHeadersFromResource(getValidResId(), target); } /** * this API method was added due to a newly discovered vulnerability. */ @Override protected boolean isValidFragment(String fragmentName) { List<Header> headers = new ArrayList<>(); /** fill fragments list */ tryObtainValidFragmentList(getValidResId(), headers); /** check id valid */ return validFragmentList.contains(fragmentName); } /** try fill list of valid fragments */ private void tryObtainValidFragmentList(int resourceId, List<Header> target) { /** check for null */ if(validFragmentList==null) { /** init */ validFragmentList = new ArrayList(); } else { /** clear */ validFragmentList.clear(); } /** load headers to list */ loadHeadersFromResource(resourceId, target); /** set headers class names to list */ for (Header header : target) { /** fill */ validFragmentList.add(header.fragment); } } /** obtain valid res id to build headers */ private int getValidResId() { /** get session manager */ _sessionManager = SessionManager.getInstance(); /** check if user is authorized */ if (_sessionManager.getCurrentUser().getWebPart().isAuthorized()) { /** if is return full preferences header */ return R.xml.settings_preferences_header_logged_in; } else { /** else return short header */ return R.xml.settings_preferences_header_logged_out; } } } 
0
Jan 14 '16 at 18:27
source share

Here is my headers_preferences.xml file:

 <?xml version="1.0" encoding="utf-8"?> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs1Fragment" android:title="Change Your Name" /> <header android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs2Fragment" android:title="Change Your Group' Name" /> <header android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs3Fragment" android:title="Change Map View" /> </preference-headers> 

In my PreferencesActivity, where the isValidFragment code happens, I just turned it on my head:

 @Override protected boolean isValidFragment(String fragmentName) { // return AppPreferencesFragment.class.getName().contains(fragmentName); return fragmentName.contains (AppPreferencesFragment.class.getName()); } 

As long as I use the AppPreferencesFragment line at the beginning of all the names of my fragments, they are all checked just fine.

0
Sep 21 '16 at 1:09
source share



All Articles