试一下这个...这就是我们检查片段有效性的方法。
protected boolean isValidFragment(String fragmentName) {
return StockPreferenceFragment.class.getName().equals(fragmentName);
}
return true
,那么这就毫无意义了。这就好像白宫授权每个走过公共门口的人,但只有那些在那里工作的人才能正确通过return true
。 - davidcesarino出于纯粹的好奇心,你也可以这样做:
@Override
protected boolean isValidFragment(String fragmentName) {
return MyPreferenceFragmentA.class.getName().equals(fragmentName)
|| MyPreferenceFragmentB.class.getName().equals(fragmentName)
|| // ... Finish with your last fragment.
;}
我发现可以在我的头部资源加载时从中获取我的片段名称的副本:
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);
}
}
这样我就不需要记住在代码中埋藏的片段列表,如果我想要更新它们,也不需要手动更新。
我曾希望直接使用getHeaders()
和现有的标题列表,但似乎在onBuildHeaders()
之后活动被销毁,在调用isValidFragment()
之前重新创建。
这可能是因为我测试的Nexus 7实际上并不支持双窗格首选项活动。因此也需要静态列表成员。
由于新发现的漏洞,该API被添加。请参见http://ibm.co/1bAA8kF或http://ibm.co/IDm2Es
2013年12月10日 "我们最近向Android安全团队披露了一个新漏洞[...]更准确地说,任何使用导出活动扩展PreferenceActivity类的应用程序都自动处于易受攻击的状态。Android KitKat中提供了补丁。如果你想知道为什么你的代码现在无法工作,那是由于Android KitKat补丁需要应用程序重写新方法PreferenceActivity.isValidFragment,该方法已添加到Android Framework中。"--来自上面第一个链接
我不确定Lane的实现是否没有被讨论这里中提到的漏洞,但如果是的话,我认为更好的解决方案是避免使用静态列表,只需执行以下操作:
@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;
}
已通过实际4.4设备验证:
(1) 如果您的proguard.cfg文件有这行代码(很多人都定义了):
-keep public class com.fullpackage.MyPreferenceFragment
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class EditPreferencesHC extends PreferenceActivity {
...
protected boolean isValidFragment (String fragmentName) {
return "com.fullpackage.MyPreferenceFragment".equals(fragmentName);
}
}
我的解决方案(而不是创建类的ArrayList),因为加载的片段应该是PreferenceFragment.class的子类,所以在@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;
}
这是我的解决方案:
如果您使用额外的方式启动首选项活动 - onBuildHeaders() 方法将失败!(使用下面的启动意图额外参数 - 为什么? - 简单,因为 onBuildHeaders() 从未被调用):
Intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMEN,Fragment.class.getName()); Intent.putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true);
这是示例类:
/**
* 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;
}
}
}
<?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>
@Override
protected boolean isValidFragment(String fragmentName)
{
// return AppPreferencesFragment.class.getName().contains(fragmentName);
return fragmentName.contains (AppPreferencesFragment.class.getName());
}
@Override
protected boolean isValidFragment (String fragmentName) {
for (Class<?> cls : ImePreferences.class.getDeclaredClasses()) {
if (cls.getName().equals(fragmentName)){return true;}
}
return false;
}