Move from arbitrary resource filters to fix ones

Resource filters are used when generating additional APK containing only
specific resources.
The previous UI allowed for any type of filters, but we are moving to a
simpler way with fixed filters.
The first one is the density. Selecting the filter will generate 4 APKs per
application: default (all resources), hdpi (only hdpi/nodpi and default
resources), mdpi, ldpi.
diff --git a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
index ef74fe7..47b8f48 100644
--- a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
+++ b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
@@ -17,6 +17,7 @@
 package com.android.ant;
 
 import com.android.sdklib.internal.project.ApkConfigurationHelper;
+import com.android.sdklib.internal.project.ApkSettings;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 
@@ -28,7 +29,6 @@
 
 import java.io.File;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
 
 /**
@@ -38,7 +38,7 @@
  *
  */
 public final class AaptExecLoopTask extends Task {
-    
+
     private String mExecutable;
     private String mCommand;
     private String mManifest;
@@ -55,7 +55,7 @@
     public void setExecutable(String executable) {
         mExecutable = executable;
     }
-    
+
     /**
      * Sets the value of the "command" attribute.
      * @param command the value.
@@ -63,7 +63,7 @@
     public void setCommand(String command) {
         mCommand = command;
     }
-    
+
     /**
      * Sets the value of the "manifest" attribute.
      * @param manifest the value.
@@ -71,7 +71,7 @@
     public void setManifest(Path manifest) {
         mManifest = manifest.toString();
     }
-    
+
     /**
      * Sets the value of the "resources" attribute.
      * @param resources the value.
@@ -79,7 +79,7 @@
     public void setResources(Path resources) {
         mResources = resources.toString();
     }
-    
+
     /**
      * Sets the value of the "assets" attribute.
      * @param assets the value.
@@ -87,7 +87,7 @@
     public void setAssets(Path assets) {
         mAssets = assets.toString();
     }
-    
+
     /**
      * Sets the value of the "androidjar" attribute.
      * @param androidJar the value.
@@ -95,7 +95,7 @@
     public void setAndroidjar(Path androidJar) {
         mAndroidJar = androidJar.toString();
     }
-    
+
     /**
      * Sets the value of the "outfolder" attribute.
      * @param outFolder the value.
@@ -103,7 +103,7 @@
     public void setOutfolder(Path outFolder) {
         mOutFolder = outFolder.toString();
     }
-    
+
     /**
      * Sets the value of the "basename" attribute.
      * @param baseName the value.
@@ -111,19 +111,19 @@
     public void setBasename(String baseName) {
         mBaseName = baseName;
     }
-    
+
     /*
      * (non-Javadoc)
-     * 
+     *
      * Executes the loop. Based on the values inside default.properties, this will
      * create alternate temporary ap_ files.
-     * 
+     *
      * @see org.apache.tools.ant.Task#execute()
      */
     @Override
     public void execute() throws BuildException {
         Project taskProject = getProject();
-        
+
         // first do a full resource package
         createPackage(null /*configName*/, null /*resourceFilter*/);
 
@@ -132,12 +132,15 @@
         File baseDir = taskProject.getBaseDir();
         ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(),
                 PropertyType.DEFAULT);
-        
-        Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties);
-        if (apkConfigs.size() > 0) {
-            Set<Entry<String, String>> entrySet = apkConfigs.entrySet();
-            for (Entry<String, String> entry : entrySet) {
-                createPackage(entry.getKey(), entry.getValue());
+
+
+        ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties);
+        if (apkSettings != null) {
+            Map<String, String> apkFilters = apkSettings.getResourceFilters();
+            if (apkFilters.size() > 0) {
+                for (Entry<String, String> entry : apkFilters.entrySet()) {
+                    createPackage(entry.getKey(), entry.getValue());
+                }
             }
         }
     }
@@ -164,19 +167,19 @@
         ExecTask task = new ExecTask();
         task.setExecutable(mExecutable);
         task.setFailonerror(true);
-        
+
         // aapt command. Only "package" is supported at this time really.
         task.createArg().setValue(mCommand);
-        
+
         // filters if needed
         if (configName != null && resourceFilter != null) {
             task.createArg().setValue("-c");
             task.createArg().setValue(resourceFilter);
         }
-        
+
         // force flag
         task.createArg().setValue("-f");
-        
+
         // manifest location
         task.createArg().setValue("-M");
         task.createArg().setValue(mManifest);
@@ -187,18 +190,18 @@
             task.createArg().setValue("-S");
             task.createArg().setValue(mResources);
         }
-        
+
         // assets location. This may not exists, and aapt doesn't like it, so we check first.
         File assets = new File(mAssets);
         if (assets.isDirectory()) {
             task.createArg().setValue("-A");
             task.createArg().setValue(mAssets);
         }
-        
+
         // android.jar
         task.createArg().setValue("-I");
         task.createArg().setValue(mAndroidJar);
-        
+
         // out file. This is based on the outFolder, baseName, and the configName (if applicable)
         String filename;
         if (configName != null && resourceFilter != null) {
@@ -206,15 +209,15 @@
         } else {
             filename = mBaseName + ".ap_";
         }
-        
+
         File file = new File(mOutFolder, filename);
         task.createArg().setValue("-F");
         task.createArg().setValue(file.getAbsolutePath());
-        
+
         // final setup of the task
         task.setProject(taskProject);
         task.setOwningTarget(getOwningTarget());
-        
+
         // execute it.
         task.execute();
     }
diff --git a/tools/anttasks/src/com/android/ant/ApkBuilderTask.java b/tools/anttasks/src/com/android/ant/ApkBuilderTask.java
index 3a15368..b2c445d 100644
--- a/tools/anttasks/src/com/android/ant/ApkBuilderTask.java
+++ b/tools/anttasks/src/com/android/ant/ApkBuilderTask.java
@@ -20,6 +20,7 @@
 import com.android.apkbuilder.internal.ApkBuilderImpl;
 import com.android.apkbuilder.internal.ApkBuilderImpl.ApkFile;
 import com.android.sdklib.internal.project.ApkConfigurationHelper;
+import com.android.sdklib.internal.project.ApkSettings;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 
@@ -35,7 +36,6 @@
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
 
 public class ApkBuilderTask extends Task {
@@ -214,11 +214,13 @@
             ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(),
                     PropertyType.DEFAULT);
 
-            Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties);
-            if (apkConfigs.size() > 0) {
-                Set<Entry<String, String>> entrySet = apkConfigs.entrySet();
-                for (Entry<String, String> entry : entrySet) {
-                    createApk(apkBuilder, entry.getKey(), entry.getValue(), path);
+            ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties);
+            if (apkSettings != null) {
+                Map<String, String> apkFilters = apkSettings.getResourceFilters();
+                if (apkFilters.size() > 0) {
+                    for (Entry<String, String> entry : apkFilters.entrySet()) {
+                        createApk(apkBuilder, entry.getKey(), entry.getValue(), path);
+                    }
                 }
             }
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
index b49ee6e..1ed5123 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
@@ -34,6 +34,7 @@
 import com.android.prefs.AndroidLocation.AndroidLocationException;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.project.ApkSettings;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
@@ -303,11 +304,15 @@
                 return referencedProjects;
             }
 
-            // get the extra configs for the project.
-            // The map contains (name, filter) where 'name' is a name to be used in the apk filename,
-            // and filter is the resource filter to be used in the aapt -c parameters to restrict
-            // which resource configurations to package in the apk.
-            Map<String, String> configs = Sdk.getCurrent().getProjectApkConfigs(project);
+            // get the APK configs for the project.
+            ApkSettings apkSettings = Sdk.getCurrent().getApkSettings(project);
+            Set<Entry<String, String>> apkfilters = null;
+            if (apkSettings != null) {
+                Map<String, String> filterMap = apkSettings.getResourceFilters();
+                if (filterMap != null && filterMap.size() > 0) {
+                    apkfilters = filterMap.entrySet();
+                }
+            }
 
             // do some extra check, in case the output files are not present. This
             // will force to recreate them.
@@ -320,19 +325,20 @@
                     mPackageResources = true;
                     mBuildFinalPackage = true;
                 } else {
-                    // if the full package is present, we check the filtered resource packages as well
-                    if (configs != null) {
-                        Set<Entry<String, String>> entrySet = configs.entrySet();
-
-                        for (Entry<String, String> entry : entrySet) {
+                    // if the full package is present, we check the filtered resource packages
+                    // as well
+                    if (apkfilters != null) {
+                        for (Entry<String, String> entry : apkfilters) {
                             String filename = String.format(AndroidConstants.FN_RESOURCES_S_AP_,
                                     entry.getKey());
 
                             tmp = outputFolder.findMember(filename);
                             if (tmp == null || (tmp instanceof IFile &&
                                     tmp.exists() == false)) {
-                                String msg = String.format(Messages.s_Missing_Repackaging, filename);
-                                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+                                String msg = String.format(Messages.s_Missing_Repackaging,
+                                        filename);
+                                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
+                                        project, msg);
                                 mPackageResources = true;
                                 mBuildFinalPackage = true;
                                 break;
@@ -360,11 +366,9 @@
                     String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
                     AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
                     mBuildFinalPackage = true;
-                } else if (configs != null) {
+                } else if (apkfilters != null) {
                     // if the full apk is present, we check the filtered apk as well
-                    Set<Entry<String, String>> entrySet = configs.entrySet();
-
-                    for (Entry<String, String> entry : entrySet) {
+                    for (Entry<String, String> entry : apkfilters) {
                         String filename = ProjectHelper.getApkFilename(project, entry.getKey());
 
                         tmp = outputFolder.findMember(filename);
@@ -411,9 +415,8 @@
                 // notified.
                 finalPackage.delete();
 
-                if (configs != null) {
-                    Set<Entry<String, String>> entrySet = configs.entrySet();
-                    for (Entry<String, String> entry : entrySet) {
+                if (apkfilters != null) {
+                    for (Entry<String, String> entry : apkfilters) {
                         String packageFilepath = osBinPath + File.separator +
                                 ProjectHelper.getApkFilename(project, entry.getKey());
 
@@ -477,9 +480,8 @@
                         }
 
                         // now do the same thing for all the configured resource packages.
-                        if (configs != null) {
-                            Set<Entry<String, String>> entrySet = configs.entrySet();
-                            for (Entry<String, String> entry : entrySet) {
+                        if (apkfilters != null) {
+                            for (Entry<String, String> entry : apkfilters) {
                                 String outPathFormat = osBinPath + File.separator +
                                         AndroidConstants.FN_RESOURCES_S_AP_;
                                 String outPath = String.format(outPathFormat, entry.getKey());
@@ -527,12 +529,11 @@
                 }
 
                 // now do the same thing for all the configured resource packages.
-                if (configs != null) {
+                if (apkfilters != null) {
                     String resPathFormat = osBinPath + File.separator +
                             AndroidConstants.FN_RESOURCES_S_AP_;
 
-                    Set<Entry<String, String>> entrySet = configs.entrySet();
-                    for (Entry<String, String> entry : entrySet) {
+                    for (Entry<String, String> entry : apkfilters) {
                         // make the filename for the resource package.
                         String resPath = String.format(resPathFormat, entry.getKey());
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java
index 7dc4ae2..a88f864 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java
@@ -18,7 +18,7 @@
 
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.sdklib.IAndroidTarget;
-import com.android.sdkuilib.internal.widgets.ApkConfigWidget;
+import com.android.sdklib.internal.project.ApkSettings;
 import com.android.sdkuilib.internal.widgets.SdkTargetSelector;
 
 import org.eclipse.core.resources.IProject;
@@ -27,14 +27,14 @@
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.ui.IWorkbenchPropertyPage;
 import org.eclipse.ui.dialogs.PropertyPage;
 
-import java.util.Map;
-
 /**
  * Property page for "Android" project.
  * This is accessible from the Package Explorer when right clicking a project and choosing
@@ -45,7 +45,7 @@
 
     private IProject mProject;
     private SdkTargetSelector mSelector;
-    private ApkConfigWidget mApkConfigWidget;
+    private Button mSplitByDensity;
 
     public AndroidPropertyPage() {
         // pass
@@ -72,13 +72,13 @@
 
         mSelector = new SdkTargetSelector(top, targets);
 
-        l = new Label(top, SWT.SEPARATOR | SWT.HORIZONTAL);
-        l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        Group g = new Group(top, SWT.NONE);
+        g.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        g.setLayout(new GridLayout(1, false));
+        g.setText("APK Generation");
 
-        l = new Label(top, SWT.NONE);
-        l.setText("Project APK Configurations");
-
-        mApkConfigWidget = new ApkConfigWidget(top);
+        mSplitByDensity = new Button(g, SWT.CHECK);
+        mSplitByDensity.setText("One APK per density");
 
         // fill the ui
         Sdk currentSdk = Sdk.getCurrent();
@@ -89,9 +89,9 @@
                 mSelector.setSelection(target);
             }
 
-            // get the apk configurations
-            Map<String, String> configs = currentSdk.getProjectApkConfigs(mProject);
-            mApkConfigWidget.fillTable(configs);
+            // get the project settings
+            ApkSettings settings = currentSdk.getApkSettings(mProject);
+            mSplitByDensity.setSelection(settings.isSplitByDpi());
         }
 
         mSelector.setSelectionListener(new SelectionAdapter() {
@@ -114,8 +114,10 @@
     public boolean performOk() {
         Sdk currentSdk = Sdk.getCurrent();
         if (currentSdk != null) {
-            currentSdk.setProject(mProject, mSelector.getSelected(),
-                    mApkConfigWidget.getApkConfigs());
+            ApkSettings apkSettings = new ApkSettings();
+            apkSettings.setSplitByDensity(mSplitByDensity.getSelection());
+
+            currentSdk.setProject(mProject, mSelector.getSelected(), apkSettings);
         }
 
         return true;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
index f2e883d..f93b6c5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
@@ -30,6 +30,7 @@
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.avd.AvdManager;
 import com.android.sdklib.internal.project.ApkConfigurationHelper;
+import com.android.sdklib.internal.project.ApkSettings;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 
@@ -68,8 +69,8 @@
             new HashMap<IProject, IAndroidTarget>();
     private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap =
             new HashMap<IAndroidTarget, AndroidTargetData>();
-    private final HashMap<IProject, Map<String, String>> mProjectApkConfigMap =
-            new HashMap<IProject, Map<String, String>>();
+    private final HashMap<IProject, ApkSettings> mApkSettingsMap =
+            new HashMap<IProject, ApkSettings>();
     private final String mDocBaseUrl;
 
     /**
@@ -193,11 +194,9 @@
      * apk configurations should not be updated.
      */
     public void setProject(IProject project, IAndroidTarget target,
-            Map<String, String> apkConfigMap) {
+            ApkSettings settings) {
         synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
             boolean resolveProject = false;
-            boolean compileProject = false;
-            boolean cleanProject = false;
 
             ProjectProperties properties = ProjectProperties.load(
                     project.getLocation().toOSString(), PropertyType.DEFAULT);
@@ -222,16 +221,18 @@
                 }
             }
 
-            if (apkConfigMap != null) {
-                // save the apk configs in the project persistent property
-                cleanProject = ApkConfigurationHelper.setConfigs(properties, apkConfigMap);
-
-                // put it in a local map for easy access.
-                mProjectApkConfigMap.put(project, apkConfigMap);
-
-                compileProject = true;
+            // if there's no settings, force default values (to reset possibly changed
+            // values in a previous call.
+            if (settings == null) {
+                settings = new ApkSettings();
             }
 
+            // save the project settings into the project persistent property
+            ApkConfigurationHelper.setProperties(properties, settings);
+
+            // put it in a local map for easy access.
+            mApkSettingsMap.put(project, settings);
+
             // we are done with the modification. Save the property file.
             try {
                 properties.save();
@@ -242,17 +243,14 @@
 
             if (resolveProject) {
                 // force a resolve of the project by updating the classpath container.
+                // This will also force a recompile.
                 IJavaProject javaProject = JavaCore.create(project);
                 AndroidClasspathContainerInitializer.updateProjects(
                         new IJavaProject[] { javaProject });
-            } else if (compileProject) {
-                // If there was removed configs, we clean instead of build
-                // (to remove the obsolete ap_ and apk file from removed configs).
+            } else {
+                // always do a full clean/build.
                 try {
-                    project.build(cleanProject ?
-                                IncrementalProjectBuilder.CLEAN_BUILD :
-                                IncrementalProjectBuilder.FULL_BUILD,
-                            null);
+                    project.build(IncrementalProjectBuilder.CLEAN_BUILD, null);
                 } catch (CoreException e) {
                     // failed to build? force resolve instead.
                     IJavaProject javaProject = JavaCore.create(project);
@@ -316,10 +314,10 @@
 
         if (sdkStorage != null) {
             synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
-                Map<String, String> configMap = ApkConfigurationHelper.getConfigs(properties);
+                ApkSettings settings = ApkConfigurationHelper.getSettings(properties);
 
-                if (configMap != null) {
-                    sdkStorage.mProjectApkConfigMap.put(project, configMap);
+                if (settings != null) {
+                    sdkStorage.mApkSettingsMap.put(project, settings);
                 }
             }
         }
@@ -392,13 +390,11 @@
     }
 
     /**
-     * Returns the configuration map for a given project.
-     * <p/>The Map key are name to be used in the apk filename, while the values are comma separated
-     * config values. The config value can be passed directly to aapt through the -c option.
+     * Returns the APK settings for a given project.
      */
-    public Map<String, String> getProjectApkConfigs(IProject project) {
+    public ApkSettings getApkSettings(IProject project) {
         synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
-            return mProjectApkConfigMap.get(project);
+            return mApkSettingsMap.get(project);
         }
     }
 
@@ -505,7 +501,7 @@
 
             // now remove the project for the maps.
             mProjectTargetMap.remove(project);
-            mProjectApkConfigMap.remove(project);
+            mApkSettingsMap.remove(project);
         }
     }
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java
index 0f630c4..5f8a8d5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java
@@ -19,6 +19,7 @@
 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard.ExportWizardPage;
+import com.android.sdklib.internal.project.ApkSettings;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.swt.SWT;
@@ -68,17 +69,17 @@
     private Text mDestination;
     private boolean mFatalSigningError;
     private FormText mDetailText;
-    /** The Apk Config map for the current project */
-    private Map<String, String> mApkConfig;
     private ScrolledComposite mScrolledComposite;
-    
+
+    private ApkSettings mApkSettings;
+
     private String mKeyDetails;
     private String mDestinationDetails;
 
     protected KeyCheckPage(ExportWizard wizard, String pageName) {
         super(pageName);
         mWizard = wizard;
-        
+
         setTitle("Destination and key/certificate checks");
         setDescription(""); // TODO
     }
@@ -93,7 +94,7 @@
         GridLayout gl = new GridLayout(3, false);
         gl.verticalSpacing *= 3;
         composite.setLayout(gl);
-        
+
         GridData gd;
 
         new Label(composite, SWT.NONE).setText("Destination APK file:");
@@ -110,26 +111,26 @@
             @Override
             public void widgetSelected(SelectionEvent e) {
                 FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE);
-                
+
                 fileDialog.setText("Destination file name");
                 // get a default apk name based on the project
                 String filename = ProjectHelper.getApkFilename(mWizard.getProject(),
                         null /*config*/);
                 fileDialog.setFileName(filename);
-        
+
                 String saveLocation = fileDialog.open();
                 if (saveLocation != null) {
                     mDestination.setText(saveLocation);
                 }
             }
         });
-        
+
         mScrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL);
         mScrolledComposite.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
         gd.horizontalSpan = 3;
         mScrolledComposite.setExpandHorizontal(true);
         mScrolledComposite.setExpandVertical(true);
-        
+
         mDetailText = new FormText(mScrolledComposite, SWT.NONE);
         mScrolledComposite.setContent(mDetailText);
 
@@ -139,18 +140,18 @@
                 updateScrolling();
             }
         });
-        
+
         setControl(composite);
     }
-    
+
     @Override
     void onShow() {
         // fill the texts with information loaded from the project.
         if ((mProjectDataChanged & DATA_PROJECT) != 0) {
             // reset the destination from the content of the project
             IProject project = mWizard.getProject();
-            mApkConfig = Sdk.getCurrent().getProjectApkConfigs(project);
-            
+            mApkSettings = Sdk.getCurrent().getApkSettings(project);
+
             String destination = ProjectHelper.loadStringProperty(project,
                     ExportWizard.PROPERTY_DESTINATION);
             String filename = ProjectHelper.loadStringProperty(project,
@@ -159,7 +160,7 @@
                 mDestination.setText(destination + File.separator + filename);
             }
         }
-        
+
         // if anything change we basically reload the data.
         if (mProjectDataChanged != 0) {
             mFatalSigningError = false;
@@ -170,7 +171,7 @@
             mPrivateKey = null;
             mCertificate = null;
             mKeyDetails = null;
-    
+
             if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) {
                 int validity = mWizard.getValidity();
                 StringBuilder sb = new StringBuilder(
@@ -196,13 +197,13 @@
                             mWizard.getKeyAlias(),
                             new KeyStore.PasswordProtection(
                                     mWizard.getKeyPassword().toCharArray()));
-                    
+
                     if (entry != null) {
                         mPrivateKey = entry.getPrivateKey();
                         mCertificate = (X509Certificate)entry.getCertificate();
                     } else {
                         setErrorMessage("Unable to find key.");
-                        
+
                         setPageComplete(false);
                     }
                 } catch (FileNotFoundException e) {
@@ -220,33 +221,33 @@
                 } catch (IOException e) {
                     onException(e);
                 }
-                
+
                 if (mPrivateKey != null && mCertificate != null) {
                     Calendar expirationCalendar = Calendar.getInstance();
                     expirationCalendar.setTime(mCertificate.getNotAfter());
                     Calendar today = Calendar.getInstance();
-                    
+
                     if (expirationCalendar.before(today)) {
                         mKeyDetails = String.format(
                                 "<p>Certificate expired on %s</p>",
                                 mCertificate.getNotAfter().toString());
-                        
+
                         // fatal error = nothing can make the page complete.
                         mFatalSigningError = true;
-        
+
                         setErrorMessage("Certificate is expired.");
                         setPageComplete(false);
                     } else {
                         // valid, key/cert: put it in the wizard so that it can be finished
                         mWizard.setSigningInfo(mPrivateKey, mCertificate);
-        
+
                         StringBuilder sb = new StringBuilder(String.format(
                                 "<p>Certificate expires on %s.</p>",
                                 mCertificate.getNotAfter().toString()));
-                        
+
                         int expirationYear = expirationCalendar.get(Calendar.YEAR);
                         int thisYear = today.get(Calendar.YEAR);
-                        
+
                         if (thisYear + 25 < expirationYear) {
                             // do nothing
                         } else {
@@ -258,14 +259,14 @@
                                         "<p>The Certificate expires in %1$s %2$s.</p>",
                                         count, count == 1 ? "year" : "years"));
                             }
-                            
+
                             sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>");
                             sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>");
                             sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, ");
                             sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>");
                             sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
                         }
-                        
+
                         mKeyDetails = sb.toString();
                     }
                 } else {
@@ -277,7 +278,7 @@
 
         onDestinationChange(true /*forceDetailUpdate*/);
     }
-    
+
     /**
      * Callback for destination field edition
      * @param forceDetailUpdate if true, the detail {@link FormText} is updated even if a fatal
@@ -319,12 +320,12 @@
 
             // display the list of files that will actually be created
             Map<String, String[]> apkFileMap = getApkFileMap(file);
-            
+
             // display them
             boolean fileExists = false;
             StringBuilder sb = new StringBuilder(String.format(
                     "<p>This will create the following files:</p>"));
-            
+
             Set<Entry<String, String[]>> set = apkFileMap.entrySet();
             for (Entry<String, String[]> entry : set) {
                 String[] apkArray = entry.getValue();
@@ -360,7 +361,7 @@
             updateDetailText();
         }
     }
-    
+
     /**
      * Updates the scrollbar to match the content of the {@link FormText} or the new size
      * of the {@link ScrolledComposite}.
@@ -372,41 +373,40 @@
             mScrolledComposite.layout();
         }
     }
-    
+
     private void updateDetailText() {
         StringBuilder sb = new StringBuilder("<form>");
         if (mKeyDetails != null) {
             sb.append(mKeyDetails);
         }
-        
+
         if (mDestinationDetails != null && mFatalSigningError == false) {
             sb.append(mDestinationDetails);
         }
-        
+
         sb.append("</form>");
-        
+
         mDetailText.setText(sb.toString(), true /* parseTags */,
                 true /* expandURLs */);
 
         mDetailText.getParent().layout();
 
         updateScrolling();
-
     }
 
     /**
      * Creates the list of destination filenames based on the content of the destination field
      * and the list of APK configurations for the project.
-     * 
+     *
      * @param file File name from the destination field
      * @return A list of destination filenames based <code>file</code> and the list of APK
      *         configurations for the project.
      */
     private Map<String, String[]> getApkFileMap(File file) {
         String filename = file.getName();
-        
+
         HashMap<String, String[]> map = new HashMap<String, String[]>();
-        
+
         // add the default APK filename
         String[] apkArray = new String[ExportWizard.APK_COUNT];
         apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
@@ -415,29 +415,32 @@
         map.put(null, apkArray);
 
         // add the APKs for each APK configuration.
-        if (mApkConfig != null && mApkConfig.size() > 0) {
-            // remove the extension.
-            int index = filename.lastIndexOf('.');
-            String base = filename.substring(0, index);
-            String extension = filename.substring(index);
-            
-            Set<Entry<String, String>> set = mApkConfig.entrySet();
-            for (Entry<String, String> entry : set) {
-                apkArray = new String[ExportWizard.APK_COUNT];
-                apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
-                        mWizard.getProject(), entry.getKey());
-                apkArray[ExportWizard.APK_FILE_DEST] = base + "-" + entry.getKey() + extension;
-                map.put(entry.getKey(), apkArray);
+        if (mApkSettings != null) {
+            Map<String, String> apkFilters = mApkSettings.getResourceFilters();
+            if (apkFilters.size() > 0) {
+                // remove the extension from the user-chosen filename
+                int index = filename.lastIndexOf('.');
+                String base = filename.substring(0, index);
+                String extension = filename.substring(index);
+
+                for (Entry<String, String> entry : apkFilters.entrySet()) {
+                    apkArray = new String[ExportWizard.APK_COUNT];
+                    apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
+                            mWizard.getProject(), entry.getKey());
+                    apkArray[ExportWizard.APK_FILE_DEST] = base + "-" + //$NON-NLS-1$
+                            entry.getKey() + extension;
+                    map.put(entry.getKey(), apkArray);
+                }
             }
         }
-        
+
         return map;
     }
-    
+
     @Override
     protected void onException(Throwable t) {
         super.onException(t);
-        
+
         mKeyDetails = String.format("ERROR: %1$s", ExportWizard.getExceptionMessage(t));
     }
 }
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java
index eeab46a..4ba6fa6 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java
@@ -16,91 +16,33 @@
 
 package com.android.sdklib.internal.project;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
 
 /**
  * Helper class to read and write Apk Configuration into a {@link ProjectProperties} file.
  */
 public class ApkConfigurationHelper {
-    /** Prefix for property names for config definition. This prevents having config named
-     * after other valid properties such as "target". */
-    final static String CONFIG_PREFIX = "apk-config-";
-
     /**
-     * Reads the Apk Configurations from a {@link ProjectProperties} file and returns them as a map.
-     * <p/>If there are no defined configurations, the returned map will be empty.
-     * @return a map of apk configurations. The map contains (name, filter) where name is
-     * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
-     * resource configuration to include in the apk (see aapt -c) 
+     * Reads the project settings from a {@link ProjectProperties} file and returns them as a
+     * {@link ApkSettings} object.
      */
-    public static Map<String, String> getConfigs(ProjectProperties properties) {
-        HashMap<String, String> configMap = new HashMap<String, String>();
+    public static ApkSettings getSettings(ProjectProperties properties) {
+        ApkSettings apkSettings = new ApkSettings();
 
-        // get the list of configs.
-        String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
-        if (configList != null) {
-            // this is a comma separated list
-            String[] configs = configList.split(","); //$NON-NLS-1$
-            
-            // read the value of each config and store it in a map
-            for (String config : configs) {
-                config = config.trim();
-                String configValue = properties.getProperty(CONFIG_PREFIX + config);
-                if (configValue != null) {
-                    configMap.put(config, configValue);
-                }
-            }
-        }
-        
-        return configMap;
+        boolean splitByDensity = Boolean.parseBoolean(properties.getProperty(
+                ProjectProperties.PROPERTY_SPLIT_BY_DENSITY));
+        apkSettings.setSplitByDensity(splitByDensity);
+
+
+        return apkSettings;
     }
-    
+
     /**
-     * Writes the Apk Configurations from a given map into a {@link ProjectProperties}.
-     * @param properties the {@link ProjectProperties} in which to store the apk configurations. 
-     * @param configMap a map of apk configurations. The map contains (name, filter) where name is
-     * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
-     * resource configuration to include in the apk (see aapt -c) 
-     * @return true if the {@link ProjectProperties} contained Apk Configuration that were not
-     * present in the map. 
+     * Sets the content of a {@link ApkSettings} into a {@link ProjectProperties}.
+     * @param properties the {@link ProjectProperties} in which to store the settings.
+     * @param settings the project settings to store.
      */
-    public static boolean setConfigs(ProjectProperties properties, Map<String, String> configMap) {
-        // load the current configs, in order to remove the value properties for each of them
-        // in case a config was removed.
-        
-        // get the list of configs.
-        String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
-
-        boolean hasRemovedConfig = false;
-
-        if (configList != null) {
-            // this is a comma separated list
-            String[] configs = configList.split(","); //$NON-NLS-1$
-            
-            for (String config : configs) {
-                config = config.trim();
-                if (configMap.containsKey(config) == false) {
-                    hasRemovedConfig = true;
-                    properties.removeProperty(CONFIG_PREFIX + config);
-                }
-            }
-        }
-        
-        // now add the properties.
-        Set<Entry<String, String>> entrySet = configMap.entrySet();
-        StringBuilder sb = new StringBuilder();
-        for (Entry<String, String> entry : entrySet) {
-            if (sb.length() > 0) {
-                sb.append(",");
-            }
-            sb.append(entry.getKey());
-            properties.setProperty(CONFIG_PREFIX + entry.getKey(), entry.getValue());
-        }
-        properties.setProperty(ProjectProperties.PROPERTY_APK_CONFIGS, sb.toString());
-        
-        return hasRemovedConfig;
+    public static void setProperties(ProjectProperties properties, ApkSettings settings) {
+        properties.setProperty(ProjectProperties.PROPERTY_SPLIT_BY_DENSITY,
+                Boolean.toString(settings.isSplitByDpi()));
     }
 }
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java
new file mode 100644
index 0000000..c96e223
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 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.android.sdklib.internal.project;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Settings for multiple APK generation.
+ */
+public class ApkSettings {
+    private boolean mSplitByDpi = false;
+
+    public ApkSettings() {
+    }
+
+    /**
+     * Returns a map of configuration filters to be used by the -c option of aapt.
+     * <p/>The map stores (key, value) pairs where the keys is a filename modifier and the value
+     * is the parameter to pass to aapt through the -c option.
+     * @return a map, always. It can however be empty.
+     */
+    public Map<String, String> getResourceFilters() {
+        Map<String, String> map = new HashMap<String, String>();
+        if (mSplitByDpi) {
+            map.put("hdpi", "hdpi,nodpi");
+            map.put("mdpi", "mdpi,nodpi");
+            map.put("ldpi", "ldpi,nodpi");
+        }
+
+        return map;
+    }
+
+    /**
+     * Indicates whether APKs should be generate for each dpi level.
+     */
+    public boolean isSplitByDpi() {
+        return mSplitByDpi;
+    }
+
+    public void setSplitByDensity(boolean split) {
+        mSplitByDpi = split;
+    }
+}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
index b3c172b..5b35d50 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
@@ -35,14 +35,17 @@
 public final class ProjectProperties {
     /** The property name for the project target */
     public final static String PROPERTY_TARGET = "target";
-    public final static String PROPERTY_APK_CONFIGS = "apk.configurations";
+
     public final static String PROPERTY_SDK = "sdk.dir";
     // LEGACY - compatibility with 1.6 and before
     public final static String PROPERTY_SDK_LEGACY = "sdk-location";
+
     public final static String PROPERTY_APP_PACKAGE = "application.package";
     // LEGACY - compatibility with 1.6 and before
     public final static String PROPERTY_APP_PACKAGE_LEGACY = "application-package";
 
+    public final static String PROPERTY_SPLIT_BY_DENSITY = "split.density";
+
     public static enum PropertyType {
         BUILD("build.properties", BUILD_HEADER),
         DEFAULT("default.properties", DEFAULT_HEADER),
@@ -107,17 +110,8 @@
 //               1-------10--------20--------30--------40--------50--------60--------70--------80
         COMMENT_MAP.put(PROPERTY_TARGET,
                 "# Project target.\n");
-        COMMENT_MAP.put(PROPERTY_APK_CONFIGS,
-                "# apk configurations. This property allows creation of APK files with limited\n" +
-                "# resources. For example, if your application contains many locales and\n" +
-                "# you wish to release multiple smaller apks instead of a large one, you can\n" +
-                "# define configuration to create apks with limited language sets.\n" +
-                "# Format is a comma separated list of configuration names. For each\n" +
-                "# configuration, a property will declare the resource configurations to\n" +
-                "# include. Example:\n" +
-                "#     " + PROPERTY_APK_CONFIGS +"=european,northamerica\n" +
-                "#     " + ApkConfigurationHelper.CONFIG_PREFIX + "european=en,fr,it,de,es\n" +
-                "#     " + ApkConfigurationHelper.CONFIG_PREFIX + "northamerica=en,es\n");
+        COMMENT_MAP.put(PROPERTY_SPLIT_BY_DENSITY,
+                "# Indicates whether an apk should be generated for each density.\n");
         COMMENT_MAP.put(PROPERTY_SDK,
                 "# location of the SDK. This is only used by Ant\n" +
                 "# For customization when using a Version Control System, please read the\n" +
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java
deleted file mode 100644
index be241a5..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2009 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.android.sdkuilib.internal.widgets;
-
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.VerifyEvent;
-import org.eclipse.swt.events.VerifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Edit dialog to create/edit APK configuration. The dialog displays 2 text fields for the config
- * name and its filter.
- */
-class ApkConfigEditDialog extends Dialog implements ModifyListener, VerifyListener {
-
-    private String mName;
-    private String mFilter;
-    private Text mNameField;
-    private Text mFilterField;
-    private Button mOkButton;
-    
-    /**
-     * Creates an edit dialog with optional initial values for the name and filter.
-     * @param name optional value for the name. Can be null.
-     * @param filter optional value for the filter. Can be null.
-     * @param parentShell the parent shell.
-     */
-    protected ApkConfigEditDialog(String name, String filter, Shell parentShell) {
-        super(parentShell);
-        mName = name;
-        mFilter = filter;
-    }
-    
-    /**
-     * Returns the name of the config. This is only valid if the user clicked OK and {@link #open()}
-     * returned {@link Window#OK}
-     */
-    public String getName() {
-        return mName;
-    }
-    
-    /**
-     * Returns the filter for the config. This is only valid if the user clicked OK and
-     * {@link #open()} returned {@link Window#OK}
-     */
-    public String getFilter() {
-        return mFilter;
-    }
-    
-    @Override
-    protected Control createContents(Composite parent) {
-        Control control = super.createContents(parent);
-
-        mOkButton = getButton(IDialogConstants.OK_ID);
-        validateButtons();
-
-        return control;
-    }
-    
-    @Override
-    protected Control createDialogArea(Composite parent) {
-        Composite composite = new Composite(parent, SWT.NONE);
-        GridLayout layout;
-        composite.setLayout(layout = new GridLayout(2, false));
-        layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
-        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
-        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
-        layout.horizontalSpacing = convertHorizontalDLUsToPixels(
-                IDialogConstants.HORIZONTAL_SPACING);
-
-        composite.setLayoutData(new GridData(GridData.FILL_BOTH));
-        
-        Label l = new Label(composite, SWT.NONE);
-        l.setText("Name");
-        
-        mNameField = new Text(composite, SWT.BORDER);
-        mNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-        mNameField.addVerifyListener(this);
-        if (mName != null) {
-            mNameField.setText(mName);
-        }
-        mNameField.addModifyListener(this);
-
-        l = new Label(composite, SWT.NONE);
-        l.setText("Filter");
-        
-        mFilterField = new Text(composite, SWT.BORDER);
-        mFilterField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-        if (mFilter != null) {
-            mFilterField.setText(mFilter);
-        }
-        mFilterField.addVerifyListener(this);
-        mFilterField.addModifyListener(this);
-        
-        applyDialogFont(composite);
-        return composite;
-    }
-    
-    /**
-     * Validates the OK button based on the content of the 2 text fields.
-     */
-    private void validateButtons() {
-        mOkButton.setEnabled(mNameField.getText().trim().length() > 0 &&
-                mFilterField.getText().trim().length() > 0);
-    }
-
-    @Override
-    protected void okPressed() {
-        mName = mNameField.getText();
-        mFilter = mFilterField.getText().trim();
-        super.okPressed();
-    }
-
-    /**
-     * Callback for text modification in the 2 text fields.
-     */
-    public void modifyText(ModifyEvent e) {
-        validateButtons();
-    }
-
-    /**
-     * Callback to ensure the content of the text field are proper.
-     */
-    public void verifyText(VerifyEvent e) {
-        Text source = ((Text)e.getSource());
-        if (source == mNameField) {
-            // check for a-zA-Z0-9.
-            final String text = e.text;
-            final int len = text.length();
-            for (int i = 0 ; i < len; i++) {
-                char letter = text.charAt(i);
-                if (letter > 255 || Character.isLetterOrDigit(letter) == false) {
-                    e.doit = false;
-                    return;
-                }
-            }
-        } else if (source == mFilterField) {
-            // we can't validate the content as its typed, but we can at least ensure the characters
-            // are valid. Same as mNameFiled + the comma.
-            final String text = e.text;
-            final int len = text.length();
-            for (int i = 0 ; i < len; i++) {
-                char letter = text.charAt(i);
-                if (letter > 255 || (Character.isLetterOrDigit(letter) == false && letter != ',')) {
-                    e.doit = false;
-                    return;
-                }
-            }
-        }
-    }
-}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java
deleted file mode 100644
index a05f9bd..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2009 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.android.sdkuilib.internal.widgets;
-
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The APK Configuration widget is a table that is added to the given parent composite.
- * <p/>
- * To use, create it using {@link #ApkConfigWidget(Composite)} then
- * call {@link #fillTable(Map)} to set the initial list of configurations.
- */
-public class ApkConfigWidget {
-    private final static int INDEX_NAME = 0;
-    private final static int INDEX_FILTER = 1;
-    
-    private Table mApkConfigTable;
-    private Button mEditButton;
-    private Button mDelButton;
-
-    public ApkConfigWidget(final Composite parent) {
-        final Composite apkConfigComp = new Composite(parent, SWT.NONE);
-        apkConfigComp.setLayoutData(new GridData(GridData.FILL_BOTH));
-        apkConfigComp.setLayout(new GridLayout(2, false));
-        
-        mApkConfigTable = new Table(apkConfigComp, SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
-        mApkConfigTable.setHeaderVisible(true);
-        mApkConfigTable.setLinesVisible(true);
-
-        GridData data = new GridData();
-        data.grabExcessVerticalSpace = true;
-        data.grabExcessHorizontalSpace = true;
-        data.horizontalAlignment = GridData.FILL;
-        data.verticalAlignment = GridData.FILL;
-        mApkConfigTable.setLayoutData(data);
-
-        // create the table columns
-        final TableColumn column0 = new TableColumn(mApkConfigTable, SWT.NONE);
-        column0.setText("Name");
-        column0.setWidth(100);
-        final TableColumn column1 = new TableColumn(mApkConfigTable, SWT.NONE);
-        column1.setText("Configuration");
-        column1.setWidth(100);
-
-        Composite buttonComp = new Composite(apkConfigComp, SWT.NONE);
-        buttonComp.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-        GridLayout gl;
-        buttonComp.setLayout(gl = new GridLayout(1, false));
-        gl.marginHeight = gl.marginWidth = 0;
-
-        Button newButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
-        newButton.setText("New...");
-        newButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
-        mEditButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
-        mEditButton.setText("Edit...");
-        mEditButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
-        mDelButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
-        mDelButton.setText("Delete");
-        mDelButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-        
-        newButton.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-                ApkConfigEditDialog dlg = new ApkConfigEditDialog(null /*name*/, null /*filter*/,
-                        apkConfigComp.getShell());
-                if (dlg.open() == Dialog.OK) {
-                    TableItem item = new TableItem(mApkConfigTable, SWT.NONE);
-                    item.setText(INDEX_NAME, dlg.getName());
-                    item.setText(INDEX_FILTER, dlg.getFilter());
-                    
-                    onSelectionChanged();
-                }
-            }
-        });
-        
-        mEditButton.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-                // get the current selection (single mode so we don't care about any item beyond
-                // index 0).
-                TableItem[] items = mApkConfigTable.getSelection();
-                if (items.length != 0) {
-                    ApkConfigEditDialog dlg = new ApkConfigEditDialog(
-                            items[0].getText(INDEX_NAME), items[0].getText(INDEX_FILTER),
-                            apkConfigComp.getShell());
-                    if (dlg.open() == Dialog.OK) {
-                        items[0].setText(INDEX_NAME, dlg.getName());
-                        items[0].setText(INDEX_FILTER, dlg.getFilter());
-                    }
-                }
-            }
-        });
-        
-        mDelButton.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-                // get the current selection (single mode so we don't care about any item beyond
-                // index 0).
-                int[] indices = mApkConfigTable.getSelectionIndices();
-                if (indices.length != 0) {
-                    TableItem item = mApkConfigTable.getItem(indices[0]);
-                    if (MessageDialog.openQuestion(parent.getShell(),
-                            "Apk Configuration deletion",
-                            String.format(
-                                    "Are you sure you want to delete configuration '%1$s'?",
-                                    item.getText(INDEX_NAME)))) {
-                        // delete the item.
-                        mApkConfigTable.remove(indices[0]);
-                        
-                        onSelectionChanged();
-                    }
-                }
-            }
-        });
-        
-        // Add a listener to resize the column to the full width of the table
-        mApkConfigTable.addControlListener(new ControlAdapter() {
-            @Override
-            public void controlResized(ControlEvent e) {
-                Rectangle r = mApkConfigTable.getClientArea();
-                column0.setWidth(r.width * 30 / 100); // 30%  
-                column1.setWidth(r.width * 70 / 100); // 70%
-            }
-        });
-        
-        // add a selection listener on the table, to enable/disable buttons.
-        mApkConfigTable.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-                onSelectionChanged();
-            }
-        });
-    }
-    
-    public void fillTable(Map<String, String> apkConfigMap) {
-        // get the names in a list so that we can sort them.
-        if (apkConfigMap != null) {
-            Set<String> keys = apkConfigMap.keySet();
-            String[] keyArray = keys.toArray(new String[keys.size()]);
-            Arrays.sort(keyArray);
-            
-            for (String key : keyArray) {
-                TableItem item = new TableItem(mApkConfigTable, SWT.NONE);
-                item.setText(INDEX_NAME, key);
-                item.setText(INDEX_FILTER, apkConfigMap.get(key));
-            }
-        }
-        
-        onSelectionChanged();
-    }
-
-    public Map<String, String> getApkConfigs() {
-        // go through all the items from the table and fill a new map
-        HashMap<String, String> map = new HashMap<String, String>();
-        
-        TableItem[] items = mApkConfigTable.getItems();
-        for (TableItem item : items) {
-            map.put(item.getText(INDEX_NAME), item.getText(INDEX_FILTER));
-        }
-
-        return map;
-    }
-    
-    /**
-     * Handles table selection changes.
-     */
-    private void onSelectionChanged() {
-        if (mApkConfigTable.getSelectionCount() > 0) {
-            mEditButton.setEnabled(true);
-            mDelButton.setEnabled(true);
-        } else {
-            mEditButton.setEnabled(false);
-            mDelButton.setEnabled(false);
-        }
-    }
-}