web-dev-qa-db-fra.com

aPI Android isValidFragment 19

Lorsque je teste mon application avec Android KitKat, je rencontre une erreur dans PreferenceActivity.

Les sous-classes de PreferenceActivity doivent remplacer isValidFragment (String) pour vérifier que la classe Fragment est valide! com.crbin1.labeltodo.ActivityPreference n'a pas vérifié si le fragment com.crbin1.labeltodo.StockPreferenceFragment est valide

Dans la documentation, je trouve l'explication suivante

protégé booléen isValidFragment (String fragmentName) 

Ajouté dans le niveau 19 de l'API

Les sous-classes doivent surcharger cette méthode et vérifier que le fragment donné est un type valide à attacher à cette activité. L'implémentation par défaut renvoie true pour les applications construites pour Android: targetSdkVersion plus ancien que KitKat. Pour les versions ultérieures, une exception sera lancée.

Je ne trouve aucun exemple pour résoudre le problème.

49
crbin1

Essayez ceci ... voici comment nous vérifions la validité de fragment.

protected boolean isValidFragment(String fragmentName) {
  return StockPreferenceFragment.class.getName().equals(fragmentName);
}
65
user2098324

Par pure curiosité, vous pouvez également le faire:

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

;}
24
davidcesarino

J'ai découvert que je pouvais récupérer une copie de mes noms de fragments à partir de la ressource en-tête lors de son chargement:

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);
    }
}

De cette façon, je n'ai pas besoin de penser à mettre à jour une liste de fragments enfouis dans le code si je veux les mettre à jour.

J'espérais utiliser directement getHeaders() et la liste d'en-têtes existante, mais il semble que l'activité soit détruite après onBuildHeaders() et recréée avant l'appel de isValidFragment().

C'est peut-être parce que le Nexus 7 sur lequel je teste ne réalise pas d'activités de préférence à deux volets. D'où la nécessité du membre de la liste statique également.

20
lane

Cette API a été ajoutée en raison d'une vulnérabilité récemment découverte. Veuillez consulter http://ibm.co/1bAA8kF ou http://ibm.co/IDm2Es

10 décembre 2013 "Nous avons récemment révélé une nouvelle vulnérabilité à l'équipe de sécurité Android. [...] Pour être plus précis, toute application qui étend la classe PreferenceActivity à l'aide d'une activité exportée était automatiquement vulnérable. Un correctif a été fourni dans Android KitKat. je me demandais pourquoi votre code est maintenant cassé, cela est dû au correctif Android KitKat qui oblige les applications à remplacer la nouvelle méthode, PreferenceActivity.isValidFragment, qui a été ajoutée au cadre Android. - Du premier lien ci-dessus

18
Roee Hay

Vérifié avec un appareil réel 4.4:

(1) si votre fichier proguard.cfg a cette ligne ( que beaucoup définissent quand même ):

-keep public class com.fullpackage.MyPreferenceFragment

(2) que la mise en œuvre la plus efficace serait:

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

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

   }
}
3
Amir Uval

Je ne sais pas si l'implémentation de lane est exempte des vulnérabilités discutées ici mais si c'est le cas, je pense qu'une meilleure solution serait d'éviter d'utiliser cette liste statique et de simplement procéder comme suit:

 @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
Ofek Ron

Voici mon fichier headers_preferences.xml: 

<?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>  

Dans mon activité PreferencesActivity où se trouve le code isValidFragment, je viens de le retourner:

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

Tant que j'utilise la chaîne AppPreferencesFragment au début de tous mes noms de fragments, ils valident tous parfaitement.

0
James Schrumpf

ma solution (au lieu de créer ArrayList of class) puisque les fragments chargés sont supposés être une sous-classe de PreferenceFragment.class exécuter cette vérification dans la méthode.

@Override
protected boolean isValidFragment(String fragmentName) {
    try {
        Class cls = Class.forName(fragmentName);
        return (cls.getSuperclass().equals(PreferenceFragment.class));
                                  // true if superclass is PreferenceFragmnet
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return false;
}
0
ItzikH

c'est ma solution:

  • si vous avez besoin d'en-têtes de reconstruction dynamiques 
  • si vous utilisez des extras pour démarrer une activité de préférence, l’approche onBuildHeaders () échouera! (avec les extras d'intention de démarrage ci-dessous - pourquoi ??? - simple car onBuildHeaders () n'est jamais appelée):

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

Voici un exemple de classe:

/**
 * 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
ceph3us