All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jayway.maven.plugins.android.standalonemojos.ManifestUpdateMojo Maven / Gradle / Ivy

There is a newer version: 4.0.0-rc.2
Show newest version
package com.jayway.maven.plugins.android.standalonemojos;

import com.jayway.maven.plugins.android.AbstractAndroidMojo;
import com.jayway.maven.plugins.android.common.AndroidExtension;
import com.jayway.maven.plugins.android.common.XmlHelper;
import com.jayway.maven.plugins.android.configuration.Manifest;
import com.jayway.maven.plugins.android.configuration.UsesSdk;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;

/**
 * Updates various version attributes present in the AndroidManifest.xml file.
 *
 * @author [email protected]
 * @author [email protected]
 * @author Manfred Moser 
 */
@Mojo( name = "manifest-update", defaultPhase = LifecyclePhase.PROCESS_RESOURCES )
public class ManifestUpdateMojo extends AbstractAndroidMojo
{
    // basic attributes
    private static final String ATTR_VERSION_NAME = "android:versionName";
    private static final String ATTR_VERSION_CODE = "android:versionCode";
    private static final String ATTR_SHARED_USER_ID = "android:sharedUserId";
    private static final String ATTR_DEBUGGABLE = "android:debuggable";

    // supports-screens attributes
    private static final String ATTR_SCREEN_DENSITY = "android:screenDensity";
    private static final String ATTR_SCREEN_SIZE = "android:screenSize";

    // compatible-screens attributes
    private static final String ATTR_ANY_DENSITY = "android:anyDensity";
    private static final String ATTR_SMALL_SCREENS = "android:smallScreens";
    private static final String ATTR_NORMAL_SCREENS = "android:normalScreens";
    private static final String ATTR_LARGE_SCREENS = "android:largeScreens";
    private static final String ATTR_XLARGE_SCREENS = "android:xlargeScreens";
    private static final String ATTR_RESIZEABLE = "android:resizeable";
    private static final String ATTR_REQUIRES_SMALLEST_WIDTH_DP = "android:requiresSmallestWidthDp";
    private static final String ATTR_LARGEST_WIDTH_LIMIT_DP = "android:largestWidthLimitDp";
    private static final String ATTR_COMPATIBLE_WIDTH_LIMIT_DP = "android:compatibleWidthLimitDp";

    // uses-sdk attributes
    private static final String ATTR_MIN_SDK_VERSION = "android:minSdkVersion";
    private static final String ATTR_MAX_SDK_VERSION = "android:maxSdkVersion";
    private static final String ATTR_TARGET_SDK_VERSION = "android:targetSdkVersion";

    // provider attributes
    private static final String ATTR_NAME = "android:name";
    private static final String ATTR_AUTHORITIES = "android:authorities";
    // application attributes
    private static final String ATTR_APPLICATION_ICON = "android:icon";
    private static final String ATTR_APPLICATION_LABEL = "android:label";
    private static final String ATTR_APPLICATION_THEME = "android:theme";
    
    private static final String ELEM_APPLICATION = "application";
    private static final String ELEM_PROVIDER = "provider";
    private static final String ELEM_SUPPORTS_SCREENS = "supports-screens";
    private static final String ELEM_COMPATIBLE_SCREENS = "compatible-screens";
    private static final String ELEM_SCREEN = "screen";
    private static final String ELEM_USES_SDK = "uses-sdk";

    // version encoding 
    private static final int NUMBER_OF_DIGITS_FOR_VERSION_POSITION = 3;
    

    /**
     * Configuration for the manifest-update goal.
     * 

* You can configure this mojo to update the following basic manifest attributes: *

*

* android:versionName on the manifest element. * android:versionCode on the manifest element. * android:sharedUserId on the manifest element. * android:debuggable on the application element. *

*

* Moreover, you may specify custom values for the supports-screens and * compatible-screens elements. This is useful if you're using custom build * profiles to build APKs tailored to specific screen configurations. Values passed via POM * configuration for these elements will be merged with whatever is found in the Manifest file. * Values defined in the POM will take precedence. *

*

* Note: This process will reformat the AndroidManifest.xml per JAXP * {@link Transformer} defaults if updates are made to the manifest. *

* You can configure attributes in the plugin configuration like so *

*

     *   <plugin>
     *     <groupId>com.jayway.maven.plugins.android.generation2</groupId>
     *     <artifactId>android-maven-plugin</artifactId>
     *     <executions>
     *       <execution>
     *         <id>update-manifest</id>
     *         <goals>
     *           <goal>manifest-update</goal>
     *         </goals>
     *         <configuration>
     *           <manifest>
     *             <versionName></versionName>
     *             <versionCode>123</versionCode>
     *             <versionCodeAutoIncrement>true|false</versionCodeAutoIncrement>
     *             <versionCodeUpdateFromVersion>true|false</versionCodeUpdateFromVersion>
     *             <sharedUserId>anId</sharedUserId>
     *             <debuggable>true|false</debuggable>
     *
     *             <supports-screens>
     *               <anyDensity>true</anyDensity>
     *               <xlargeScreens>false</xlargeScreens>
     *             </supports-screens>
     *
     *             <compatible-screens>
     *               <compatible-screen>
     *                 <screenSize>small</screenSize>
     *                 <screenDensity>ldpi</screenDensity>
     *               </compatible-screen>
     *             </compatible-screens>
     *           </manifest>
     *         </configuration>
     *       </execution>
     *     </executions>
     *   </plugin>
     * 
*

* or use properties set in the pom or settings file or supplied as command line parameter. Add * "android." in front of the property name for command line usage. All parameters follow a * manifest.* naming convention. *

*/ @Parameter private Manifest manifest; /** * Update the android:versionName with the specified parameter. If left empty it * will use the version number of the project. Exposed via the project property * android.manifest.versionName. */ @Parameter( property = "android.manifest.versionName", defaultValue = "${project.version}" ) protected String manifestVersionName; /** * Update the android:versionCode attribute with the specified parameter. Exposed via * the project property android.manifest.versionCode. */ @Parameter( property = "android.manifest.versionCode" ) protected Integer manifestVersionCode; /** * Auto increment the android:versionCode attribute with each build. The value is * exposed via the project property android.manifest.versionCodeAutoIncrement and * the resulting value as android.manifest.versionCode. */ @Parameter( property = "android.manifest.versionCodeAutoIncrement", defaultValue = "false" ) private Boolean manifestVersionCodeAutoIncrement = false; /** * Update the android:icon attribute with the specified parameter. Exposed via * the project property android.manifest.appIcon. */ @Parameter( property = "android.manifest.applicationIcon" ) private String manifestApplicationIcon; /** * Update the android:label attribute with the specified parameter. Exposed via * the project property android.manifest.appLabel. */ @Parameter( property = "android.manifest.applicationLabel" ) private String manifestApplicationLabel; /** * Update the android:theme attribute with the specified parameter. Exposed via * the project property android.manifest.applicationTheme. */ @Parameter( property = "android.manifest.applicationTheme" ) private String manifestApplicationTheme; /** * Update the android:versionCode attribute automatically from the project version * e.g 3.2.1 will become version code 3002001. As described in this blog post * http://www.simpligility.com/2010/11/release-version-management-for-your-android-application/ * but done without using resource filtering. The value is exposed via the project property * property android.manifest.versionCodeUpdateFromVersion and the resulting value * as android.manifest.versionCode. * For the purpose of generating the versionCode, if a version element is missing it is presumed to be 0. * The maximum values for the version increment and version minor values are 999, * the version major should be no larger than 2000. Any other suffixes do not * participate in the version code generation. */ @Parameter( property = "android.manifest.versionCodeUpdateFromVersion", defaultValue = "false" ) protected Boolean manifestVersionCodeUpdateFromVersion = false; /** * Update the android:sharedUserId attribute with the specified parameter. If * specified, exposes the project property android.manifest.sharedUserId. */ @Parameter( property = "android.manifest.sharedUserId" ) protected String manifestSharedUserId; /** * Update the android:debuggable attribute with the specified parameter. Exposed via * the project property android.manifest.debuggable. */ @Parameter( property = "android.manifest.debuggable" ) protected Boolean manifestDebuggable; /** * For a given provider (named by android:name update the android:authorities * attribute for the provider. Exposed via the project property android.manifest.providerAuthorities. */ @Parameter( property = "android.manifest.providerAuthorities" ) protected Properties manifestProviderAuthorities; protected SupportsScreens manifestSupportsScreens; protected List manifestCompatibleScreens; /** * Update the uses-sdk tag. It can be configured to change: android:minSdkVersion, * android:maxSdkVersion and android:targetSdkVersion */ protected UsesSdk manifestUsesSdk; private String parsedVersionName; private Integer parsedVersionCode; private boolean parsedVersionCodeAutoIncrement; private String parsedApplicationIcon; private String parsedApplicationLabel; private String parsedApplicationTheme; private Boolean parsedVersionCodeUpdateFromVersion; private String parsedSharedUserId; private Boolean parsedDebuggable; private SupportsScreens parsedSupportsScreens; private List parsedCompatibleScreens; private Properties parsedProviderAuthorities; private UsesSdk parsedUsesSdk; /** * The modified AndroidManifest.xml file. */ @Parameter( property = "android.manifestFile", defaultValue = "${project.basedir}/AndroidManifest.xml" ) protected File updatedManifestFile; /** * @throws MojoExecutionException * @throws MojoFailureException */ public void execute() throws MojoExecutionException, MojoFailureException { if ( ! AndroidExtension.isAndroidPackaging( project.getPackaging() ) ) { return; // skip, not an android project. } if ( androidManifestFile == null ) { return; // skip, no androidmanifest.xml defined (rare case) } parseConfiguration(); getLog().info( "Attempting to update manifest " + androidManifestFile ); getLog().debug( " usesSdk=" + parsedUsesSdk ); getLog().debug( " versionName=" + parsedVersionName ); getLog().debug( " versionCode=" + parsedVersionCode ); getLog().debug( " versionCodeAutoIncrement=" + parsedVersionCodeAutoIncrement ); getLog().debug( " versionCodeUpdateFromVersion=" + parsedVersionCodeUpdateFromVersion ); getLog().debug( " applicationIcon=" + parsedApplicationIcon ); getLog().debug( " applicationLabel=" + parsedApplicationLabel ); getLog().debug( " applicationTheme=" + parsedApplicationTheme ); getLog().debug( " sharedUserId=" + parsedSharedUserId ); getLog().debug( " debuggable=" + parsedDebuggable ); getLog().debug( " providerAuthorities: " + parsedProviderAuthorities ); getLog().debug( " supports-screens: " + ( parsedSupportsScreens == null ? "not set" : "set" ) ); getLog().debug( " compatible-screens: " + ( parsedCompatibleScreens == null ? "not set" : "set" ) ); if ( ! androidManifestFile.exists() ) { return; // skip, no AndroidManifest.xml file found. } try { updateManifest( androidManifestFile ); } catch ( IOException e ) { throw new MojoFailureException( "XML I/O error: " + androidManifestFile, e ); } catch ( ParserConfigurationException e ) { throw new MojoFailureException( "Unable to prepare XML parser", e ); } catch ( SAXException e ) { throw new MojoFailureException( "Unable to parse XML: " + androidManifestFile, e ); } catch ( TransformerException e ) { throw new MojoFailureException( "Unable write XML: " + androidManifestFile, e ); } } private void parseConfiguration() { // manifest element found in plugin config in pom if ( manifest != null ) { if ( StringUtils.isNotEmpty( manifest.getVersionName() ) ) { parsedVersionName = manifest.getVersionName(); } else { parsedVersionName = manifestVersionName; } if ( manifest.getVersionCode() != null ) { parsedVersionCode = manifest.getVersionCode(); } else { parsedVersionCode = manifestVersionCode; } if ( manifest.getVersionCodeAutoIncrement() != null ) { parsedVersionCodeAutoIncrement = manifest.getVersionCodeAutoIncrement(); } else { parsedVersionCodeAutoIncrement = manifestVersionCodeAutoIncrement; } if ( manifest.getVersionCodeUpdateFromVersion() != null ) { parsedVersionCodeUpdateFromVersion = manifest.getVersionCodeUpdateFromVersion(); } else { parsedVersionCodeUpdateFromVersion = manifestVersionCodeUpdateFromVersion; } if ( StringUtils.isNotEmpty( manifest.getApplicationIcon() ) ) { parsedApplicationIcon = manifest.getApplicationIcon(); } else { parsedApplicationIcon = manifestApplicationIcon; } if ( StringUtils.isNotEmpty( manifest.getApplicationLabel() ) ) { parsedApplicationLabel = manifest.getApplicationLabel(); } else { parsedApplicationLabel = manifestApplicationLabel; } if ( StringUtils.isNotEmpty( manifest.getApplicationTheme() ) ) { parsedApplicationTheme = manifest.getApplicationTheme(); } else { parsedApplicationTheme = manifestApplicationTheme; } if ( StringUtils.isNotEmpty( manifest.getSharedUserId() ) ) { parsedSharedUserId = manifest.getSharedUserId(); } else { parsedSharedUserId = manifestSharedUserId; } if ( manifest.getDebuggable() != null ) { parsedDebuggable = manifest.getDebuggable(); } else { parsedDebuggable = manifestDebuggable; } if ( manifest.getSupportsScreens() != null ) { parsedSupportsScreens = manifest.getSupportsScreens(); } else { parsedSupportsScreens = manifestSupportsScreens; } if ( manifest.getCompatibleScreens() != null ) { parsedCompatibleScreens = manifest.getCompatibleScreens(); } else { parsedCompatibleScreens = manifestCompatibleScreens; } if ( manifest.getProviderAuthorities() != null ) { parsedProviderAuthorities = manifest.getProviderAuthorities(); } else { parsedProviderAuthorities = manifestProviderAuthorities; } if ( manifest.getUsesSdk() != null ) { parsedUsesSdk = manifest.getUsesSdk(); } else { parsedUsesSdk = manifestUsesSdk; } } else { parsedVersionName = manifestVersionName; parsedVersionCode = manifestVersionCode; parsedVersionCodeAutoIncrement = manifestVersionCodeAutoIncrement; parsedVersionCodeUpdateFromVersion = manifestVersionCodeUpdateFromVersion; parsedApplicationIcon = manifestApplicationIcon; parsedApplicationLabel = manifestApplicationLabel; parsedApplicationTheme = manifestApplicationTheme; parsedSharedUserId = manifestSharedUserId; parsedDebuggable = manifestDebuggable; parsedSupportsScreens = manifestSupportsScreens; parsedCompatibleScreens = manifestCompatibleScreens; parsedProviderAuthorities = manifestProviderAuthorities; parsedUsesSdk = manifestUsesSdk; } } /** * Read manifest using JAXP */ private Document readManifest( File manifestFile ) throws IOException, ParserConfigurationException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse( manifestFile ); return doc; } /** * Write manifest using JAXP transformer */ private void writeManifest( File manifestFile, Document doc ) throws IOException, TransformerException { TransformerFactory xfactory = TransformerFactory.newInstance(); Transformer xformer = xfactory.newTransformer(); xformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" ); Source source = new DOMSource( doc ); OutputStreamWriter writer = null; try { manifestFile.getParentFile().mkdirs(); String encoding = doc.getXmlEncoding() != null ? doc.getXmlEncoding() : "UTF-8"; writer = new OutputStreamWriter( new FileOutputStream( manifestFile, false ), encoding ); if ( doc.getXmlEncoding() != null && doc.getXmlVersion() != null ) { String xmldecl = String .format( "%n", doc.getXmlVersion(), doc.getXmlEncoding() ); writer.write( xmldecl ); } Result result = new StreamResult( writer ); xformer.transform( source, result ); } finally { IOUtils.closeQuietly( writer ); } } /** * * @param manifestFile * @throws IOException * @throws ParserConfigurationException * @throws SAXException * @throws TransformerException * @throws MojoFailureException */ public void updateManifest( File manifestFile ) throws IOException, ParserConfigurationException, SAXException, TransformerException, MojoFailureException { Document doc = readManifest( manifestFile ); Element manifestElement = doc.getDocumentElement(); boolean dirty = false; if ( StringUtils.isEmpty( parsedVersionName ) ) { // default to ${project.version} parsedVersionName = project.getVersion(); } Attr versionNameAttrib = manifestElement.getAttributeNode( ATTR_VERSION_NAME ); if ( versionNameAttrib == null || ! StringUtils.equals( parsedVersionName, versionNameAttrib.getValue() ) ) { getLog().info( "Setting " + ATTR_VERSION_NAME + " to " + parsedVersionName ); manifestElement.setAttribute( ATTR_VERSION_NAME, parsedVersionName ); dirty = true; } if ( ( parsedVersionCodeAutoIncrement && parsedVersionCode != null ) || ( parsedVersionCodeUpdateFromVersion && parsedVersionCode != null ) || ( parsedVersionCodeAutoIncrement && parsedVersionCodeUpdateFromVersion ) ) { throw new MojoFailureException( "versionCodeAutoIncrement, versionCodeUpdateFromVersion and versionCode " + "are mutual exclusive. They cannot be specified at the same time. Please specify either " + "versionCodeAutoIncrement, versionCodeUpdateFromVersion or versionCode!" ); } exportProperties(); if ( parsedVersionCodeAutoIncrement ) { performVersioCodeAutoIncrement( manifestElement ); dirty = true; } if ( parsedVersionCodeUpdateFromVersion ) { performVersionCodeUpdateFromVersion( manifestElement ); dirty = true; } if ( parsedVersionCode != null ) { Attr versionCodeAttr = manifestElement.getAttributeNode( ATTR_VERSION_CODE ); int currentVersionCode = 0; if ( versionCodeAttr != null ) { currentVersionCode = NumberUtils.toInt( versionCodeAttr.getValue(), 0 ); } if ( currentVersionCode != parsedVersionCode ) { getLog().info( "Setting " + ATTR_VERSION_CODE + " to " + parsedVersionCode ); manifestElement.setAttribute( ATTR_VERSION_CODE, String.valueOf( parsedVersionCode ) ); dirty = true; } project.getProperties().setProperty( "android.manifest.versionCode", String.valueOf( parsedVersionCode ) ); } if ( !StringUtils.isEmpty( parsedApplicationIcon ) ) { dirty = updateApplicationAttribute( manifestElement, ATTR_APPLICATION_ICON, parsedApplicationIcon, dirty ); project.getProperties() .setProperty( "android.manifest.applicationIcon", String.valueOf( parsedApplicationIcon ) ); } if ( ! StringUtils.isEmpty( parsedApplicationLabel ) ) { dirty = updateApplicationAttribute( manifestElement, ATTR_APPLICATION_LABEL, parsedApplicationLabel, dirty ); project.getProperties() .setProperty( "android.manifest.applicationLabel", String.valueOf( parsedApplicationLabel ) ); } if ( ! StringUtils.isEmpty( parsedApplicationTheme ) ) { dirty = updateApplicationAttribute( manifestElement, ATTR_APPLICATION_THEME, parsedApplicationTheme, dirty ); project.getProperties() .setProperty( "android.manifest.applicationTheme", String.valueOf( parsedApplicationTheme ) ); } if ( ! StringUtils.isEmpty( parsedSharedUserId ) ) { Attr sharedUserIdAttrib = manifestElement.getAttributeNode( ATTR_SHARED_USER_ID ); if ( sharedUserIdAttrib == null || ! StringUtils .equals( parsedSharedUserId, sharedUserIdAttrib.getValue() ) ) { getLog().info( "Setting " + ATTR_SHARED_USER_ID + " to " + parsedSharedUserId ); manifestElement.setAttribute( ATTR_SHARED_USER_ID, parsedSharedUserId ); dirty = true; } } if ( parsedDebuggable != null ) { NodeList appElems = manifestElement.getElementsByTagName( ELEM_APPLICATION ); // Update all application nodes. Not sure whether there will ever be more than one. for ( int i = 0; i < appElems.getLength(); ++ i ) { Node node = appElems.item( i ); getLog().info( "Testing if node " + node.getNodeName() + " is application" ); if ( node.getNodeType() == Node.ELEMENT_NODE ) { Element element = ( Element ) node; Attr debuggableAttrib = element.getAttributeNode( ATTR_DEBUGGABLE ); if ( debuggableAttrib == null || parsedDebuggable != BooleanUtils .toBoolean( debuggableAttrib.getValue() ) ) { getLog().info( "Setting " + ATTR_DEBUGGABLE + " to " + parsedDebuggable ); element.setAttribute( ATTR_DEBUGGABLE, String.valueOf( parsedDebuggable ) ); dirty = true; } } } } if ( parsedSupportsScreens != null ) { boolean madeDirty = performSupportScreenModification( doc, manifestElement ); if ( madeDirty ) { dirty = true; } } if ( parsedCompatibleScreens != null ) { getLog().info( "Setting " + ELEM_COMPATIBLE_SCREENS ); updateCompatibleScreens( doc, manifestElement ); dirty = true; } dirty = processProviderAuthorities( manifestElement, dirty ); dirty = processUsesSdk( doc, manifestElement, dirty ); if ( dirty ) { if ( updatedManifestFile != null && !manifestFile.equals( updatedManifestFile ) ) { project.getProperties().setProperty( "android.manifestFile", updatedManifestFile.getAbsolutePath() ); manifestFile = updatedManifestFile; } if ( manifestFile.exists() && ! manifestFile.delete() ) { getLog().warn( "Could not remove old " + manifestFile ); } getLog().info( "Made changes to manifest file, updating " + manifestFile ); writeManifest( manifestFile, doc ); } else { getLog().info( "No changes found to write to manifest file" ); } } private boolean processProviderAuthorities( Element manifestElement, boolean dirty ) { if ( parsedProviderAuthorities != null ) { boolean madeDirty = updateProviderAuthorities( manifestElement ); if ( madeDirty ) { dirty = true; } } return dirty; } private boolean processUsesSdk( Document doc, Element manifestElement, boolean dirty ) { if ( parsedUsesSdk != null ) { boolean madeDirty = performUsesSdkModification( doc, manifestElement ); if ( madeDirty ) { dirty = true; } } return dirty; } private boolean updateApplicationAttribute( Element manifestElement, String attribute, String value, boolean dirty ) { NodeList appElements = manifestElement.getElementsByTagName( ELEM_APPLICATION ); // Update all application nodes. Not sure whether there will ever be // more than one. for ( int i = 0; i < appElements.getLength(); ++i ) { Node node = appElements.item( i ); getLog().info( "Testing if node " + node.getNodeName() + " is application" ); if ( node.getNodeType() == Node.ELEMENT_NODE ) { Element element = (Element) node; Attr labelAttrib = element.getAttributeNode( attribute ); if ( labelAttrib == null || !value.equals( labelAttrib.getValue() ) ) { getLog().info( "Setting " + attribute + " to " + value ); element.setAttribute( attribute, String.valueOf( value ) ); dirty = true; } } } return dirty; } /** * Expose the version properties and other simple parsed manifest entries. */ private void exportProperties() { project.getProperties().setProperty( "android.manifest.versionName", parsedVersionName ); project.getProperties().setProperty( "android.manifest.versionCodeAutoIncrement", String.valueOf( parsedVersionCodeAutoIncrement ) ); project.getProperties().setProperty( "android.manifest.versionCodeUpdateFromVersion", String.valueOf( parsedVersionCodeUpdateFromVersion ) ); project.getProperties().setProperty( "android.manifest.debuggable", String.valueOf( parsedDebuggable ) ); if ( parsedSharedUserId != null ) { project.getProperties().setProperty( "android.manifest.sharedUserId", parsedSharedUserId ); } } private void performVersioCodeAutoIncrement( Element manifestElement ) { Attr versionCode = manifestElement.getAttributeNode( ATTR_VERSION_CODE ); int currentVersionCode = 0; if ( versionCode != null ) { currentVersionCode = NumberUtils.toInt( versionCode.getValue(), 0 ); } currentVersionCode++; manifestElement.setAttribute( ATTR_VERSION_CODE, String.valueOf( currentVersionCode ) ); project.getProperties().setProperty( "android.manifest.versionCode", String.valueOf( currentVersionCode ) ); } /** * If the specified version name cannot be properly parsed then fall back to * an automatic method. * If the version can be parsed then generate a version code from the * version components. In an effort to preseve uniqueness two digits * are allowed for both the minor and incremental versions. */ private void performVersionCodeUpdateFromVersion( Element manifestElement ) { String verString = project.getVersion(); getLog().debug( "Generating versionCode for " + verString ); String verCode = generateVersionCodeFromVersionName( verString ); getLog().info( "Setting " + ATTR_VERSION_CODE + " to " + verCode ); manifestElement.setAttribute( ATTR_VERSION_CODE, verCode ); project.getProperties().setProperty( "android.manifest.versionCode", String.valueOf( verCode ) ); } private String generateVersionCodeFromVersionName( String versionName ) { String[] versionNameDigits = versionName.replaceAll( "[^0-9.]", "" ).split( "\\." ); long versionCode = 0; for ( int i = 0; i < versionNameDigits.length; i++ ) { double digitMultiplayer = Math.pow( 10, i * NUMBER_OF_DIGITS_FOR_VERSION_POSITION ); String versionDigit = versionNameDigits[versionNameDigits.length - i - 1 ]; versionCode += Integer.valueOf( versionDigit ).intValue() * digitMultiplayer; } return String.valueOf( versionCode ); } private boolean performSupportScreenModification( Document doc, Element manifestElement ) { boolean dirty = false; Element supportsScreensElem = XmlHelper.getOrCreateElement( doc, manifestElement, ELEM_SUPPORTS_SCREENS ); getLog().info( "Setting " + ELEM_SUPPORTS_SCREENS ); if ( parsedSupportsScreens.getAnyDensity() != null ) { supportsScreensElem.setAttribute( ATTR_ANY_DENSITY, parsedSupportsScreens.getAnyDensity() ); dirty = true; } if ( parsedSupportsScreens.getSmallScreens() != null ) { supportsScreensElem.setAttribute( ATTR_SMALL_SCREENS, parsedSupportsScreens.getSmallScreens() ); dirty = true; } if ( parsedSupportsScreens.getNormalScreens() != null ) { supportsScreensElem.setAttribute( ATTR_NORMAL_SCREENS, parsedSupportsScreens.getNormalScreens() ); dirty = true; } if ( parsedSupportsScreens.getLargeScreens() != null ) { supportsScreensElem.setAttribute( ATTR_LARGE_SCREENS, parsedSupportsScreens.getLargeScreens() ); dirty = true; } if ( parsedSupportsScreens.getXlargeScreens() != null ) { supportsScreensElem.setAttribute( ATTR_XLARGE_SCREENS, parsedSupportsScreens.getXlargeScreens() ); dirty = true; } if ( parsedSupportsScreens.getCompatibleWidthLimitDp() != null ) { supportsScreensElem.setAttribute( ATTR_COMPATIBLE_WIDTH_LIMIT_DP, parsedSupportsScreens.getCompatibleWidthLimitDp() ); dirty = true; } if ( parsedSupportsScreens.getLargestWidthLimitDp() != null ) { supportsScreensElem .setAttribute( ATTR_LARGEST_WIDTH_LIMIT_DP, parsedSupportsScreens.getLargestWidthLimitDp() ); dirty = true; } if ( parsedSupportsScreens.getRequiresSmallestWidthDp() != null ) { supportsScreensElem.setAttribute( ATTR_REQUIRES_SMALLEST_WIDTH_DP, parsedSupportsScreens.getRequiresSmallestWidthDp() ); dirty = true; } if ( parsedSupportsScreens.getResizeable() != null ) { supportsScreensElem.setAttribute( ATTR_RESIZEABLE, parsedSupportsScreens.getResizeable() ); dirty = true; } return dirty; } private boolean performUsesSdkModification ( Document doc, Element manifestElement ) { boolean dirty = false; Element usesSdkElem = XmlHelper.getOrCreateElement( doc, manifestElement, ELEM_USES_SDK ); if ( parsedUsesSdk.getMinSdkVersion() != null ) { usesSdkElem.setAttribute( ATTR_MIN_SDK_VERSION, parsedUsesSdk.getMinSdkVersion() ); dirty = true; } if ( parsedUsesSdk.getMaxSdkVersion() != null ) { usesSdkElem.setAttribute( ATTR_MAX_SDK_VERSION, parsedUsesSdk.getMaxSdkVersion() ); dirty = true; } if ( parsedUsesSdk.getTargetSdkVersion() != null ) { usesSdkElem.setAttribute( ATTR_TARGET_SDK_VERSION, parsedUsesSdk.getTargetSdkVersion() ); dirty = true; } return dirty; } private void updateCompatibleScreens( Document doc, Element manifestElement ) { Element compatibleScreensElem = XmlHelper.getOrCreateElement( doc, manifestElement, ELEM_COMPATIBLE_SCREENS ); // read those screen elements that were already defined in the Manifest NodeList manifestScreenElems = compatibleScreensElem.getElementsByTagName( ELEM_SCREEN ); int numManifestScreens = manifestScreenElems.getLength(); ArrayList manifestScreens = new ArrayList( numManifestScreens ); for ( int i = 0; i < numManifestScreens; i++ ) { Element screenElem = ( Element ) manifestScreenElems.item( i ); CompatibleScreen screen = new CompatibleScreen(); screen.setScreenDensity( screenElem.getAttribute( ATTR_SCREEN_DENSITY ) ); screen.setScreenSize( screenElem.getAttribute( ATTR_SCREEN_SIZE ) ); manifestScreens.add( screen ); getLog().debug( "Found Manifest compatible-screen: " + screen ); } // remove all child nodes, since we'll rebuild the element XmlHelper.removeDirectChildren( compatibleScreensElem ); for ( CompatibleScreen screen : parsedCompatibleScreens ) { getLog().debug( "Found POM compatible-screen: " + screen ); } // merge those screens defined in the POM, overriding any matching screens // already defined in the Manifest HashSet mergedScreens = new HashSet(); mergedScreens.addAll( manifestScreens ); mergedScreens.addAll( parsedCompatibleScreens ); for ( CompatibleScreen screen : mergedScreens ) { getLog().debug( "Using compatible-screen: " + screen ); Element screenElem = doc.createElement( ELEM_SCREEN ); screenElem.setAttribute( ATTR_SCREEN_SIZE, screen.getScreenSize() ); screenElem.setAttribute( ATTR_SCREEN_DENSITY, screen.getScreenDensity() ); compatibleScreensElem.appendChild( screenElem ); } } private boolean updateProviderAuthorities( Element manifestElement ) { boolean dirty = false; NodeList appElems = manifestElement.getElementsByTagName( ELEM_APPLICATION ); // Update all application nodes. Not sure whether there will ever be more than one. for ( int i = 0; i < appElems.getLength(); ++ i ) { Node node = appElems.item( i ); if ( node.getNodeType() == Node.ELEMENT_NODE ) { NodeList providerElems = manifestElement.getElementsByTagName( ELEM_PROVIDER ); for ( int j = 0; j < providerElems.getLength(); ++ j ) { Node providerNode = providerElems.item( j ); if ( providerNode.getNodeType() == Node.ELEMENT_NODE ) { Element providerElem = (Element) providerNode; Attr providerName = providerElem.getAttributeNode( ATTR_NAME ); getLog().debug( "Checking provider " + providerName.getValue() ); if ( shouldPerformProviderUpdate( providerName ) ) { dirty = true; String name = providerName.getValue(); String newAuthorities = parsedProviderAuthorities.getProperty( name ); getLog().info( "Updating provider " + name + " authorities attr to " + newAuthorities ); performProviderUpdate( providerElem, newAuthorities ); } } } } } return dirty; } private boolean shouldPerformProviderUpdate( Attr providerName ) { if ( providerName == null ) { return false; } for ( String propName: parsedProviderAuthorities.stringPropertyNames() ) { if ( propName.equals( providerName.getValue() ) ) { return true; } } return false; } private void performProviderUpdate( Element providerElem, String newAuthorities ) { Attr providerAuthorities = providerElem.getAttributeNode( ATTR_AUTHORITIES ); providerAuthorities.setValue( newAuthorities ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy