package com.afwsamples.testdpc.search;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TimingLogger;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Crawl indexable fragments to index all their preferences.
* Run adb shell setprop log.tag.PreferenceCrawler_Timer VERBOSE to see timing log.
* At the time of writing, nexus 5x spends 27ms to finish crawling.
*/
public class PreferenceCrawler {
private Context mContext;
private static final String NODE_NAME_PREFERENCE_SCREEN = "PreferenceScreen";
private static final String NODE_NAME_PREFERENCE_CATEGORY = "PreferenceCategory";
private static final String TAG = "PreferenceCrawler_Timer";
public PreferenceCrawler(Context context) {
mContext = context;
}
public List<PreferenceIndex> doCrawl() {
final TimingLogger logger = new TimingLogger(TAG, "doCrawl");
List<PreferenceIndex> indexablePreferences = new ArrayList<>();
List<IndexableFragment> indexableFragments = IndexableFragments.values();
for (IndexableFragment indexableFragment : indexableFragments) {
indexablePreferences.addAll(crawlSingleIndexableResource(indexableFragment));
logger.addSplit("processed " + indexableFragment.fragmentName);
}
logger.addSplit("Finish crawling");
logger.dumpToLog();
return indexablePreferences;
}
/**
* Skim through the xml preference file.
* @return a list of indexable preference.
*/
private List<PreferenceIndex> crawlSingleIndexableResource(
IndexableFragment indexableFragment) {
List<PreferenceIndex> indexablePreferences = new ArrayList<>();
XmlPullParser parser = mContext.getResources().getXml(indexableFragment.xmlRes);
int type;
try {
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
// Parse next until start tag is found
}
String nodeName = parser.getName();
if (!NODE_NAME_PREFERENCE_SCREEN.equals(nodeName)) {
throw new RuntimeException(
"XML document must start with <PreferenceScreen> tag; found"
+ nodeName + " at " + parser.getPositionDescription());
}
final int outerDepth = parser.getDepth();
final AttributeSet attrs = Xml.asAttributeSet(parser);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
nodeName = parser.getName();
String key = PreferenceXmlUtil.getDataKey(mContext, attrs);
String title = PreferenceXmlUtil.getDataTitle(mContext, attrs);
if (NODE_NAME_PREFERENCE_CATEGORY.equals(nodeName) || TextUtils.isEmpty(key)
|| TextUtils.isEmpty(title)) {
continue;
}
PreferenceIndex indexablePreference =
new PreferenceIndex(key, title, indexableFragment.fragmentName);
indexablePreferences.add(indexablePreference);
}
} catch (XmlPullParserException | IOException | ReflectiveOperationException ex) {
Log.e(TAG, "Error in parsing a preference xml file, skip it", ex);
}
return indexablePreferences;
}
}