/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorolamobility.preflighting.checkers.missingdrawable; import java.util.HashSet; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.motorolamobility.preflighting.core.applicationdata.ApplicationData; import com.motorolamobility.preflighting.core.applicationdata.Element; import com.motorolamobility.preflighting.core.applicationdata.Element.Type; import com.motorolamobility.preflighting.core.applicationdata.ElementUtils; import com.motorolamobility.preflighting.core.applicationdata.FolderElement; import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement; import com.motorolamobility.preflighting.core.applicationdata.XMLElement; /** * Keeps information about the drawable folders into application */ public class MissingDrawableData { /* * Android manifest related constants */ private final int MIN_SDK_THRESHOLD = 3; private final int MAX_XHDPI_SDK_THRESHOLD = 9; private final String TAG_NAME_USES_SDK = "uses-sdk"; //$NON-NLS-1$ private final String TAG_NAME_SUPPORTS_SCREENS = "supports-screens"; //$NON-NLS-1$ private final String ATTRIBUTE_ANY_DENSITY = "android:anyDensity"; //$NON-NLS-1$ private final String ATTRIBUTE_MIN_SDK_VERSION = "android:minSdkVersion"; //$NON-NLS-1$ private final String ATTRIBUTE_MAX_SDK_VERSION = "android:maxSdkVersion"; //$NON-NLS-1$ private boolean standardFolderExists = false; private boolean ldpiFolderExists = false; private boolean mdpiFolderExists = false; private boolean hdpiFolderExists = false; private boolean xhdpiFolderExists = false; /** * The application manifest */ private XMLElement manifestElement; /** * xhdpi resolution, available for api level 9 or above */ private boolean isXhdpiApplicable = true; private ResourcesFolderElement resFolder; //A list of all existing drawable elements names private HashSet<String> drawableElements = new HashSet<String>(); private boolean testApplicable; public MissingDrawableData(ApplicationData appData) { this.manifestElement = appData.getManifestElement(); // Retrieve all images declared in all drawable folders List<Element> folderResElements = ElementUtils.getElementByType(appData.getRootElement(), Type.FOLDER_RES); resFolder = folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0) : null; // Check if all 4 resolution folders and the standard folder exist if (resFolder.getDrawableFolder() != null) { standardFolderExists = true; } // ldpi folder if (resFolder.getLdpiDrawableFolder() != null) { ldpiFolderExists = true; } // mdpi folder if (resFolder.getMdpiDrawableFolder() != null) { mdpiFolderExists = true; } // hdpi folder if (resFolder.getHdpiDrawableFolder() != null) { hdpiFolderExists = true; } // hdpi folder if (resFolder.getXhdpiDrawableFolder() != null) { xhdpiFolderExists = true; } // Construct a list of unique drawable elements List<FolderElement> drawableFolders = resFolder.getDrawableFolders(); for (FolderElement f : drawableFolders) { for (Element e : ElementUtils.getElementByType(f, Type.FILE_DRAWABLE)) { drawableElements.add(e.getName()); } } checkTestApplicability(); } /** * @return the resFolder */ protected ResourcesFolderElement getResFolder() { return resFolder; } /** * @return the standardFolderExists */ protected boolean isStandardFolderExists() { return standardFolderExists; } /** * @return the ldpiFolderExists */ protected boolean isLdpiFolderExists() { return ldpiFolderExists; } /** * @return the mdpiFolderExists */ protected boolean isMdpiFolderExists() { return mdpiFolderExists; } /** * @return the hdpiFolderExists */ protected boolean isHdpiFolderExists() { return hdpiFolderExists; } /** * @return the xhdpiFolderExists */ protected boolean isXhdpiFolderExists() { return xhdpiFolderExists; } /** * @return the isXhdpiApplicable */ protected boolean isXhdpiApplicable() { return isXhdpiApplicable; } /** * @return the drawableElements */ protected HashSet<String> getDrawableElements() { return drawableElements; } /** * Auxiliary method to determine is this test is applicable at all to the given application. If not, the checker will return an OK status. * If the minSdkVersion is 3 (or lower) or the <supports-screen> tag does not exit, return an OK result. * @param data The application data. * @return A boolean stating if the the test is applicable */ private boolean checkTestApplicability() { testApplicable = true; Document manifestDoc = manifestElement.getDocument(); isXhdpiApplicable = true; // Validate <uses-sdk> node. NodeList usesSdkList = manifestDoc.getElementsByTagName(TAG_NAME_USES_SDK); if (usesSdkList.getLength() > 0) { for (int i = 0; i < usesSdkList.getLength(); i++) { Node usesSdkNode = usesSdkList.item(i); Node minSdkAttribute = usesSdkNode.getAttributes().getNamedItem(ATTRIBUTE_MIN_SDK_VERSION); if (minSdkAttribute != null) { int minSdk = Integer.parseInt(minSdkAttribute.getNodeValue()); if (minSdk <= MIN_SDK_THRESHOLD) { testApplicable = false; } } else { testApplicable = false; } Node maxSdkAttribute = usesSdkNode.getAttributes().getNamedItem(ATTRIBUTE_MAX_SDK_VERSION); if (maxSdkAttribute != null) { int maxSdk = Integer.parseInt(maxSdkAttribute.getNodeValue()); if (maxSdk < MAX_XHDPI_SDK_THRESHOLD) { isXhdpiApplicable = false; } } } } else { testApplicable = false; } if (testApplicable) { // Validate <supports-screen> node. NodeList supportsScreensList = manifestDoc.getElementsByTagName(TAG_NAME_SUPPORTS_SCREENS); if (supportsScreensList.getLength() > 0) { for (int i = 0; i < supportsScreensList.getLength(); i++) { Node supportsScreensNode = supportsScreensList.item(i); Node anyDensityAttribute = supportsScreensNode.getAttributes().getNamedItem(ATTRIBUTE_ANY_DENSITY); if (anyDensityAttribute != null) { boolean anyDensity = Boolean.parseBoolean(anyDensityAttribute.getNodeValue()); if (!anyDensity) { testApplicable = false; } } } } } return testApplicable; } /** * Keep the result of {@link MissingDrawableData#checkTestApplicability() } to avoid running it for all conditions * Warning: Guarantee that {@link MissingDrawableData#checkTestApplicability() } is run once before calling this method. * @return A boolean stating if the the test is applicable */ protected boolean isTestApplicable() { return testApplicable; } /** * @return true if at least one folder (ldpi, mdpi, hdpi or xhdpi exists), false otherwise. */ public boolean atLeastOneDrawableFolderExist() { return (isLdpiFolderExists() || isMdpiFolderExists() || isHdpiFolderExists() || (isXhdpiFolderExists() && isXhdpiApplicable())); } /** * @return true if at least one folder (ldpi, mdpi, hdpi or xhdpi is missing), false otherwise. */ public boolean isMissingAtLeastOneDrawableFolder() { return (!isLdpiFolderExists() || !isMdpiFolderExists() || !isHdpiFolderExists() || (!isXhdpiFolderExists() && isXhdpiApplicable())); } }