ISVALIDFRAGMENT Android API 19
quand j'essaie mon application avec Android KitKat j'ai une erreur dans PreferenceActivity.
les sous-classes de preference activity doivent supplanter isValidFragment (String) pour vérifier que la classe de Fragment est valide! COM.crbin1.labeltodo.ActivityPreference n'a pas vérifié si fragment com.crbin1.labeltodo.StockPreferenceFragment est valide
dans la documentation je trouve l'explication suivante
protégé booléen isValidFragment (chaîne fragmentName)
ajouté au niveau API 19
Les sous-classesdevraient remplacer cette méthode et vérifier que le fragment donné est un type valide à rattacher à cette activité. L'implémentation par défaut retourne true pour les applications construites pour android: targetSdkVersion plus ancienne que KITKAT. Pour les versions suivantes, il va jeter une exception.
je ne trouve aucun exemple de résoudre le problème.
9 réponses
essayez ceci... c'est ainsi que nous vérifions la validité du fragment.
protected boolean isValidFragment(String fragmentName) {
return StockPreferenceFragment.class.getName().equals(fragmentName);
}
par pure curiosité, vous pouvez aussi le faire:
@Override
protected boolean isValidFragment(String fragmentName) {
return MyPreferenceFragmentA.class.getName().equals(fragmentName)
|| MyPreferenceFragmentB.class.getName().equals(fragmentName)
|| // ... Finish with your last fragment.
;}
j'ai trouvé que je pouvais prendre une copie de mes noms de fragments de ma ressource en-tête comme il a été chargé:
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 me rappeler de mettre à jour une liste de fragments enfouis dans le code si je veux les mettre à jour.
j'avais espéré utiliser getHeaders()
et la liste existante des en-têtes directement, mais il semble que l'activité soit détruite après onBuildHeaders()
et recréée avant isValidFragment()
est appelé.
This peut-être parce que le Nexus 7 que je teste ne fait pas réellement des activités de préférence à deux volets. D'où la nécessité d'un membre de la liste statique.
cette API a été ajoutée en raison d'une vulnérabilité nouvellement découverte. S'il vous plaît voir http://ibm.co/1bAA8kF ou http://ibm.co/IDm2Es
10 décembre 2013 "Nous avons récemment dévoilé une nouvelle vulnérabilité à L'équipe de sécurité Android. [...] Pour être plus précis, toute application qui étend la classe PreferenceActivity en utilisant une activité exportée est automatiquement vulnérable. Un patch a fourni dans Android KitKat. Si vous vous demandez pourquoi votre code est maintenant cassé, c'est en raison du patch Android KitKat qui nécessite des applications pour outrepasser la nouvelle méthode, PreferenceActivity.isValidFragment, qui a été ajouté au Cadre Android."- À partir du premier lien ci-dessus
vérifié avec un appareil réel 4.4:
(1) si votre proguard.le fichier cfg a cette ligne ( que beaucoup définissent de toute façon ):
-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);
}
}
Je ne suis pas sûr que l'implémentation de lane soit 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 faire ce qui 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;
}
c'est ma solution:
- si u besoin de dynamique de reconstruction en-têtes
-
si u utiliser des suppléments pour démarrer l'activité de préférence - onBuildHeaders() approche ne fonctionnera pas! (avec des extras d'intention de départ ci-dessous-pourquoi ??? - simple car onbuild headers () n'est jamais appelé):
intention.putExtra (Préférenceactivité.EXTRA_SHOW_FRAGMEN, Fragment.classe.getName()); Intention.putExtra (Préférenceactivité.EXTRA_NO_HEADERS, true);
C'est la classe exemple:
/**
* 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;
}
}
}
Voici mes headers_preferences.fichier 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é préférée où le code isValidFragment se produit, je l'ai juste tourné sur sa tête:
@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 les noms de mes fragments, ils valident parfaitement.
ma solution (au lieu de créer ArrayList de classe) puisque les fragments qui sont chargés suppose être sous-classe de fragment de préférence.cours effectuer ce contrôle dans le Méthode @OverRide
@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;
}