LayoutXmlProcessor.java revision 0c2ed0cbaee2f206e926bfc780b05e9f1e52b551
18e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount/*
28e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Copyright (C) 2015 The Android Open Source Project
38e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Licensed under the Apache License, Version 2.0 (the "License");
48e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * you may not use this file except in compliance with the License.
58e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * You may obtain a copy of the License at
68e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount *      http://www.apache.org/licenses/LICENSE-2.0
78e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Unless required by applicable law or agreed to in writing, software
88e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * distributed under the License is distributed on an "AS IS" BASIS,
98e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * See the License for the specific language governing permissions and
118e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * limitations under the License.
128e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount */
138e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
14fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool;
158e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
1697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport org.apache.commons.lang3.StringEscapeUtils;
178e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport org.xml.sax.SAXException;
188e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.BindingBuildInfo;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.store.LayoutFileParser;
21fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.store.ResourceBundle;
22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.writer.JavaFileWriter;
23a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
248e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.File;
258e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.FilenameFilter;
268e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.IOException;
278e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.StringWriter;
28a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.ArrayList;
298e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.util.List;
308e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.util.UUID;
318e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
328e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBContext;
338e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBException;
348e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.Marshaller;
358e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.parsers.ParserConfigurationException;
368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.xpath.XPathExpressionException;
378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount/**
398e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Processes the layout XML, stripping the binding attributes and elements
408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * and writes the information into an annotated class file for the annotation
418e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * processor to work with.
428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount */
438e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountpublic class LayoutXmlProcessor {
44a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    // hardcoded in baseAdapters
45fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount    public static final String RESOURCE_BUNDLE_PACKAGE = "android.databinding.layouts";
46a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public static final String CLASS_NAME = "DataBindingInfo";
478e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final JavaFileWriter mFileWriter;
488e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final ResourceBundle mResourceBundle;
4997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private final int mMinSdk;
5097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
518e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private boolean mProcessingComplete;
528e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private boolean mWritten;
53a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private final boolean mIsLibrary;
548e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final String mBuildId = UUID.randomUUID().toString();
55a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    // can be a list of xml files or folders that contain XML files
56a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private final List<File> mResources;
578e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
58a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public LayoutXmlProcessor(String applicationPackage, List<File> resources,
59a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            JavaFileWriter fileWriter, int minSdk, boolean isLibrary) {
608e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mFileWriter = fileWriter;
618e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mResourceBundle = new ResourceBundle(applicationPackage);
62a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mResources = resources;
6397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mMinSdk = minSdk;
64a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mIsLibrary = isLibrary;
65a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
66a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
67a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public static List<File> getLayoutFiles(List<File> resources) {
68a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        List<File> result = new ArrayList<File>();
690c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar        for (File resource : resources) {
700c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar            if (!resource.exists() || !resource.canRead()) {
710c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar                continue;
720c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar            }
73a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            if (resource.isDirectory()) {
74a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                for (File layoutFolder : resource.listFiles(layoutFolderFilter)) {
75a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    for (File xmlFile : layoutFolder.listFiles(xmlFileFilter)) {
76a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                        result.add(xmlFile);
77a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    }
78a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
79a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
80a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            } else if (xmlFileFilter.accept(resource.getParentFile(), resource.getName())) {
81a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                result.add(resource);
82a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
83a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
84a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return result;
85a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
86a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
87a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
88a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * used by the studio plugin
89a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
90a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public ResourceBundle getResourceBundle() {
91a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return mResourceBundle;
928e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
938e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
944d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount    public boolean processResources(int minSdk)
958e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            throws ParserConfigurationException, SAXException, XPathExpressionException,
968e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            IOException {
978e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (mProcessingComplete) {
988e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return false;
998e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1008e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        LayoutFileParser layoutFileParser = new LayoutFileParser();
101a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (File xmlFile : getLayoutFiles(mResources)) {
102a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
1034d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    .parseXml(xmlFile, mResourceBundle.getAppPackage(), minSdk);
104a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            if (bindingLayout != null && !bindingLayout.isEmpty()) {
1050390898cf7c4fcad255e8cfd6802f722b516cb2cGeorge Mount                mResourceBundle.addLayoutBundle(bindingLayout);
1068e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
1078e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1088e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mProcessingComplete = true;
1098e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        return true;
1108e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1118e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
112be83770af82294c1841eef414513653646b88dc6Yigit Boyar    public void writeLayoutInfoFiles(File xmlOutDir) throws JAXBException {
1138e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (mWritten) {
1148e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return;
1158e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1168e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        JAXBContext context = JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class);
1178e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        Marshaller marshaller = context.createMarshaller();
118be83770af82294c1841eef414513653646b88dc6Yigit Boyar
1198e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles()
1208e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                .values()) {
1218e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            for (ResourceBundle.LayoutFileBundle layout : layouts) {
122a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                writeXmlFile(xmlOutDir, layout, marshaller);
1238e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
1248e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1258e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mWritten = true;
1268e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1278e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
128a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout,
129a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            Marshaller marshaller) throws JAXBException {
130a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String filename = generateExportFileName(layout) + ".xml";
131a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String xml = toXML(layout, marshaller);
132a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mFileWriter.writeToFile(new File(xmlOutDir, filename), xml);
133a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
134a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
135a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public String getInfoClassFullName() {
136a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME;
137a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
138a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
139a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private String toXML(ResourceBundle.LayoutFileBundle layout, Marshaller marshaller)
1408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            throws JAXBException {
141a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        StringWriter writer = new StringWriter();
142a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        marshaller.marshal(layout, writer);
143a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return writer.getBuffer().toString();
144a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
145a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
146a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
147a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * Generates a string identifier that can uniquely identify the given layout bundle.
148a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * This identifier can be used when we need to export data about this layout bundle.
149a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
15030cc484507f3dfd7a3c56e5f785c4783e3e0da9dGeorge Mount    public String generateExportFileName(ResourceBundle.LayoutFileBundle layout) {
151a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        StringBuilder name = new StringBuilder(layout.getFileName());
152a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        name.append('-').append(layout.getDirectory());
153a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (int i = name.length() - 1; i >= 0; i--) {
154a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            char c = name.charAt(i);
1558e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            if (c == '-') {
156a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                name.deleteCharAt(i);
157a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                c = Character.toUpperCase(name.charAt(i));
158a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                name.setCharAt(i, c);
1598e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
1608e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
161a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return name.toString();
1628e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1638e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
164b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar    public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir,
165b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            /*Nullable*/ File exportClassListTo) {
166b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        writeInfoClass(sdkDir, xmlOutDir, exportClassListTo, false);
167b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar    }
168b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar
169b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar    public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir, File exportClassListTo,
170b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            boolean enableDebugLogs) {
171ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar        final String sdkPath = sdkDir == null ? null : StringEscapeUtils.escapeJava(sdkDir.getAbsolutePath());
172a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final Class annotation = BindingBuildInfo.class;
173a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final String layoutInfoPath = StringEscapeUtils.escapeJava(xmlOutDir.getAbsolutePath());
174b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        final String exportClassListToPath = exportClassListTo == null ? "" :
175b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar                StringEscapeUtils.escapeJava(exportClassListTo.getAbsolutePath());
176a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" +
177a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "import " + annotation.getCanonicalName() + ";\n\n" +
178a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " +
179a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "modulePackage=\"" + mResourceBundle.getAppPackage() + "\", " +
180ae161289a90f6ac69ffe6411ac8c09653c81d04dYigit Boyar                "sdkRoot=" + "\"" + (sdkPath == null ? "" : sdkPath) + "\"," +
181a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "layoutInfoDir=\"" + layoutInfoPath + "\"," +
182b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar                "exportClassListTo=\"" + exportClassListToPath + "\"," +
183a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "isLibrary=" + mIsLibrary + "," +
184b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar                "minSdk=" + mMinSdk + "," +
185b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar                "enableDebugLogs=" + enableDebugLogs + ")\n" +
186a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "public class " + CLASS_NAME + " {}\n";
187fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME, classString);
1888e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1898e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
190a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private static final FilenameFilter layoutFolderFilter = new FilenameFilter() {
1918e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        @Override
1928e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        public boolean accept(File dir, String name) {
1938e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return name.startsWith("layout");
1948e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1958e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    };
1968e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
197a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private static final FilenameFilter xmlFileFilter = new FilenameFilter() {
1988e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        @Override
1998e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        public boolean accept(File dir, String name) {
2008e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return name.toLowerCase().endsWith(".xml");
2018e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2028e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    };
2038e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount}
204