isValidFragment Android API 19

2019-01-08 11:14发布

When I try my app with Android KitKat I have an error in PreferenceActivity.

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

In documentation I find the following explanation

protected boolean isValidFragment (String fragmentName)

Added in API level 19

Subclasses should override this method and verify that the given fragment is a valid type to be attached to this activity. The default implementation returns true for apps built for android:targetSdkVersion older than KITKAT. For later versions, it will throw an exception.

I don't find any example to resolve the problem.

9条回答
狗以群分
2楼-- · 2019-01-08 11:44

this is my solution:

  • if u need dynamic rebuild headers
  • if u use extras to start preference activity - onBuildHeaders() approach will fail! (with below start intent extras - why ??? - simple because onBuildHeaders() is never called):

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

This is 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;
        }
    }
}
查看更多
走好不送
3楼-- · 2019-01-08 11:45

Here's 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''s Name" />  

    <header  
        android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs3Fragment"  
        android:title="Change Map View" />  

</preference-headers>  

In my PreferencesActivity where the isValidFragment code occurs, I just turned it on its 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 string at the start of all my fragment names, they all validate just fine.

查看更多
女痞
4楼-- · 2019-01-08 11:49

I found I could grab a copy of my fragment names from my header resource as 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);
    }
}

This way I don't need to remember to update a list of fragments buried in the code if I want to update them.

I had hoped to use getHeaders() and the existing list of headers directly, but it seems the activity is destroyed after onBuildHeaders() and recreated before isValidFragment() is called.

This may be because the Nexus 7 I'm testing on doesn't actually do two-pane preference activities. Hence the need for the static list member as well.

查看更多
地球回转人心会变
5楼-- · 2019-01-08 11:51

Out of pure curiosity, you can also do this as well:

@Override
protected boolean isValidFragment(String fragmentName) {
    return MyPreferenceFragmentA.class.getName().equals(fragmentName)
            || MyPreferenceFragmentB.class.getName().equals(fragmentName)
            || // ... Finish with your last fragment.

;}
查看更多
孤傲高冷的网名
6楼-- · 2019-01-08 11:53

Try this... this is how we check validity of fragment.

protected boolean isValidFragment(String fragmentName) {
  return StockPreferenceFragment.class.getName().equals(fragmentName);
}
查看更多
倾城 Initia
7楼-- · 2019-01-08 11:55

Verified with an actual 4.4 device:

(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 would be:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class EditPreferencesHC extends PreferenceActivity {
...
   protected boolean isValidFragment (String fragmentName) {

     return "com.fullpackage.MyPreferenceFragment".equals(fragmentName);

   }
}
查看更多
登录 后发表回答