FileDocCategorySizeDatePackage
ApkDeltaVisitor.javaAPI DocAndroid 1.5 API11773Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.adt.build

ApkDeltaVisitor

public class ApkDeltaVisitor extends com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor implements org.eclipse.core.resources.IResourceDeltaVisitor
Delta resource visitor looking for changes that will trigger a new packaging of an Android application.

This looks for the following changes:

  • Any change to the AndroidManifest.xml file
  • Any change inside the assets/ folder
  • Any file change inside the res/ folder
  • Any .class file change inside the output folder
  • Any change to the classes.dex inside the output folder
  • Any change to the packaged resources file inside the output folder
  • Any change to a non java/aidl file inside the source folders
  • Any change to .so file inside the lib (native library) folder

Fields Summary
private boolean
mConvertToDex
compile flag. This is set to true if one of the changed/added/removed file is a .class file. Upon visiting all the delta resources, if this flag is true, then we know we'll have to make the "classes.dex" file.
private boolean
mPackageResources
compile flag. This is set to true if one of the changed/added/removed file is a resource file. Upon visiting all the delta resources, if this flag is true, then we know we'll have to make the intermediate apk file.
private boolean
mMakeFinalPackage
Final package flag. This is set to true if one of the changed/added/removed file is a non java file (or aidl) in the resource folder. Upon visiting all the delta resources, if this flag is true, then we know we'll have to make the final package.
private ArrayList
mSourceFolders
List of source folders.
private org.eclipse.core.runtime.IPath
mOutputPath
private org.eclipse.core.runtime.IPath
mAssetPath
private org.eclipse.core.runtime.IPath
mResPath
private org.eclipse.core.runtime.IPath
mLibFolder
Constructors Summary
public ApkDeltaVisitor(BaseBuilder builder, ArrayList sourceFolders, org.eclipse.core.resources.IFolder outputfolder)
Builds the object with a specified output folder.

param
builder the xml builder using this object to visit the resource delta.
param
sourceFolders the list of source folders for the project, relative to the workspace.
param
outputfolder the output folder of the project.


                                                     
        
              
        super(builder);
        mSourceFolders = sourceFolders;
        
        if (outputfolder != null) {
            mOutputPath = outputfolder.getFullPath();
        }
        
        IResource assetFolder = builder.getProject().findMember(SdkConstants.FD_ASSETS);
        if (assetFolder != null) {
            mAssetPath = assetFolder.getFullPath();
        }

        IResource resFolder = builder.getProject().findMember(SdkConstants.FD_RESOURCES);
        if (resFolder != null) {
            mResPath = resFolder.getFullPath();
        }
        
        IResource libFolder = builder.getProject().findMember(SdkConstants.FD_NATIVE_LIBS);
        if (libFolder != null) {
            mLibFolder = libFolder.getFullPath();
        }
    
Methods Summary
public booleangetConvertToDex()

        return mConvertToDex;
    
public booleangetMakeFinalPackage()

        return mMakeFinalPackage;
    
public booleangetPackageResources()

        return mPackageResources;
    
public booleanvisit(org.eclipse.core.resources.IResourceDelta delta)
{@inheritDoc}

throws
CoreException
see
org.eclipse.core.resources.IResourceDeltaVisitor #visit(org.eclipse.core.resources.IResourceDelta)

        // if all flags are true, we can stop going through the resource delta.
        if (mConvertToDex && mPackageResources && mMakeFinalPackage) {
            return false;
        }

        // we are only going to look for changes in res/, src/ and in
        // AndroidManifest.xml since the delta visitor goes through the main
        // folder before its childre we can check when the path segment
        // count is 2 (format will be /$Project/folder) and make sure we are
        // processing res/, src/ or AndroidManifest.xml
        IResource resource = delta.getResource();
        IPath path = resource.getFullPath();
        String[] pathSegments = path.segments();
        int type = resource.getType();

        // since the delta visitor also visits the root we return true if
        // segments.length = 1
        if (pathSegments.length == 1) {
            return true;
        }

        // check the manifest.
        if (pathSegments.length == 2 &&
                AndroidConstants.FN_ANDROID_MANIFEST.equalsIgnoreCase(pathSegments[1])) {
            // if the manifest changed we have to repackage the
            // resources.
            mPackageResources = true;
            mMakeFinalPackage = true;

            // we don't want to go to the children, not like they are
            // any for this resource anyway.
            return false;
        }
        
        // check the other folders.
        if (mOutputPath != null && mOutputPath.isPrefixOf(path)) {
            // a resource changed inside the output folder.
            if (type == IResource.FILE) {
                // just check this is a .class file. Any modification will
                // trigger a change in the classes.dex file
                String ext = resource.getFileExtension();
                if (AndroidConstants.EXT_CLASS.equalsIgnoreCase(ext)) {
                    mConvertToDex = true;
                    mMakeFinalPackage = true;
    
                    // no need to check the children, as we are in a package
                    // and there can only be subpackage children containing
                    // only .class files
                    return false;
                }

                // check for a few files directly in the output folder and force
                // rebuild if they have been deleted.
                if (delta.getKind() == IResourceDelta.REMOVED) {
                    IPath parentPath = path.removeLastSegments(1);
                    if (mOutputPath.equals(parentPath)) {
                        String resourceName = resource.getName();
                        // check if classes.dex was removed
                        if (resourceName.equalsIgnoreCase(AndroidConstants.FN_CLASSES_DEX)) {
                            mConvertToDex = true;
                            mMakeFinalPackage = true;
                        } else if (resourceName.equalsIgnoreCase(
                                AndroidConstants.FN_RESOURCES_AP_) ||
                                AndroidConstants.PATTERN_RESOURCES_S_AP_.matcher(
                                        resourceName).matches()) {
                            // or if the default resources.ap_ or a configured version
                            // (resources-###.ap_) was removed.
                            mPackageResources = true;
                            mMakeFinalPackage = true;
                        }
                    }
                }
            }

            // if this is a folder, we only go visit it if we don't already know
            // that we need to convert to dex already.
            return mConvertToDex == false;
        } else if (mResPath != null && mResPath.isPrefixOf(path)) {
            // in the res folder we are looking for any file modification
            // (we don't care about folder being added/removed, only content
            // is important)
            if (type == IResource.FILE) {
                mPackageResources = true;
                mMakeFinalPackage = true;
                return false;
            }

            // for folders, return true only if we don't already know we have to
            // package the resources.
            return mPackageResources == false;
        } else if (mAssetPath != null && mAssetPath.isPrefixOf(path)) {
            // this is the assets folder that was modified.
            // we don't care what content was changed. All we care
            // about is that something changed inside. No need to visit
            // the children even.
            mPackageResources = true;
            mMakeFinalPackage = true;
            return false;
        } else if (mLibFolder != null && mLibFolder.isPrefixOf(path)) {
            // inside the native library folder. Test if the changed resource is a .so file.
            if (type == IResource.FILE &&
                    path.getFileExtension().equalsIgnoreCase(AndroidConstants.EXT_NATIVE_LIB)) {
                mMakeFinalPackage = true;
                return false; // return false for file.
            }

            // for folders, return true only if we don't already know we have to make the
            // final package.
            return mMakeFinalPackage == false;
        } else {
            // we are in a folder that is neither the resource folders, nor the output.
            // check against all the source folders, unless we already know we need to do
            // the final package.
            // This could be a source folder or a folder leading to a source folder.
            // However we only check this if we don't already know that we need to build the
            // package anyway
            if (mMakeFinalPackage == false) {
                for (IPath sourcePath : mSourceFolders) {
                    if (sourcePath.isPrefixOf(path)) {
                        // In the source folders, we are looking for any kind of
                        // modification related to file that are not java files.
                        // Also excluded are aidl files, and package.html files
                        if (type == IResource.FOLDER) {
                            // always visit the subfolders, unless the folder is not to be included
                            return ApkBuilder.checkFolderForPackaging((IFolder)resource);
                        } else if (type == IResource.FILE) {
                            if (ApkBuilder.checkFileForPackaging((IFile)resource)) {
                                mMakeFinalPackage = true;
                            }

                            return false;
                        }
                        
                    }
                }
            }
        }
        
        // if the folder is not inside one of the folders we are interested in (res, assets, output,
        // source folders), it could be a folder leading to them, so we return true.
        return true;