Commits

radim  committed 138234b

Enabled Libraries panel in project customizer. Displays and controls isLibrary flag. Sketch of a table to show referenced projects added (not yet functional).

  • Participants
  • Parent commits 3905c0a

Comments (0)

Files changed (15)

File project/src/org/netbeans/modules/android/project/AndroidGeneralData.java

     data.setProjectDirPath(project.getProjectDirectoryFile().getAbsolutePath());
     data.setPlatform(AndroidProjects.projectPlatform(project));
     data.setMainProjectDirPath(project.evaluator().getProperty(PropertyName.TEST_PROJECT_DIR.getName()));
+    data.setLibrary(project.info().isLibrary());
     return data;
   }
 
   private String projectName;
   private String projectDirPath;
   private DalvikPlatform platform;
-  protected String mainProjectDirPath;
+  private String mainProjectDirPath;
+  private boolean isLib = false;
 
   public AndroidGeneralData() {
   }
     this.mainProjectDirPath = mainProjectDirPath;
   }
 
+  public boolean isLibrary() {
+    return isLib;
+  }
+
+  public void setLibrary(boolean isLib) {
+    this.isLib = isLib;
+  }
+
   @Override
   public boolean equals(Object obj) {
     if (obj == null) {
     if (!Objects.equal(this.mainProjectDirPath, other.mainProjectDirPath)) {
       return false;
     }
+    if (this.isLib != other.isLib) {
+      return false;
+    }
     return true;
   }
 
     hash = 67 * hash + (this.projectDirPath != null ? this.projectDirPath.hashCode() : 0);
     hash = 67 * hash + (this.platform != null ? this.platform.hashCode() : 0);
     hash = 67 * hash + (this.mainProjectDirPath != null ? this.mainProjectDirPath.hashCode() : 0);
+    hash = 67 * hash + (this.isLib ? 1 : 0);
     return hash;
   }
 }

File project/src/org/netbeans/modules/android/project/AndroidInfoImpl.java

 import com.google.common.base.Strings;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.openide.filesystems.FileChangeAdapter;
   }
   
   @Override
+  public Iterable<File> getDependentProjectDirs() {
+    List<File> prjDirs = new ArrayList<File>();
+    for (Map.Entry<String, String> prop : project.evaluator().getProperties().entrySet()) {
+      // TODO sort by number?
+      if (prop.getKey().startsWith("android.library.reference.")) {
+        File libPrjDir = new File(project.getProjectDirectoryFile(), prop.getValue());
+        prjDirs.add(libPrjDir);
+      }
+    }
+    return prjDirs;
+  }
+  
+  @Override
   public String getFixDescription() {
     if (!needsRefresh) {
       return NbBundle.getMessage(AndroidInfoImpl.class, "TXT_ProjectOK");

File project/src/org/netbeans/modules/android/project/AndroidProjectImpl.java

 
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.project.ProjectCreator;
+import com.android.sdklib.internal.project.ProjectProperties;
 import org.netbeans.modules.android.project.launch.LaunchConfiguration;
 import org.netbeans.modules.android.project.queries.AndroidSharabilityQuery;
 import org.netbeans.modules.android.project.queries.AndroidTemplateAttributesProvider;
           data.getPlatform().getAndroidTarget(),
           data.getProjectName(),
           /*library*/null);
+      if (data.isLibrary()) {
+        propertyHelper.setProperty(
+          ProjectProperties.PropertyType.DEFAULT, AndroidInfoImpl.ANDROID_LIBRARY_PROPERTY, Boolean.toString(true));
+      } else {
+        propertyHelper.setProperty(
+          ProjectProperties.PropertyType.DEFAULT, AndroidInfoImpl.ANDROID_LIBRARY_PROPERTY, null);
+      }
     } else {
       prjCreator.updateTestProject(data.getProjectDirPath(), data.getMainProjectDirPath(), sdkManager);
     }

File project/src/org/netbeans/modules/android/project/AndroidProjectInfo.java

 package org.netbeans.modules.android.project;
 
 import java.beans.PropertyChangeListener;
+import java.io.File;
 
 /**
  *
    */
   boolean isTest();
   
+  /** Iterable of dependencies on project libraries. */
+  Iterable<File> getDependentProjectDirs();
+
   // TODO - maybe move update methods here from AndroidProject?
   
   /** Getter to find whether project needs to be updated using SDK tools. */

File project/src/org/netbeans/modules/android/project/AndroidSubprojectProvider.java

      * to share it for better maintenance - maybe use getSubprojects there,
      * and convert this Set to List<Key>?
      */
-     AndroidClassPath cpProvider = project.getLookup().lookup(AndroidClassPath.class);
-          if (cpProvider != null) {
-            for (File prjRoot : cpProvider.getDependentProjectDirs()) {
-              try {
-                FileObject prjRootFO = FileUtil.toFileObject(FileUtil.normalizeFile(prjRoot));
-                if (prjRootFO == null) {
-                  continue;
-                }
-                Project p = ProjectManager.getDefault().findProject(prjRootFO);
-                projectSet.add(p);
-              } catch (IOException ex) {
-                LOG.log(Level.WARNING, null, ex);
-              } catch (IllegalArgumentException ex) {
-                LOG.log(Level.WARNING, null, ex);
-              }
-            }
+    AndroidProjectInfo info = project.info();
+    if (info != null) {
+      for (File prjRoot : info.getDependentProjectDirs()) {
+        try {
+          FileObject prjRootFO = FileUtil.toFileObject(FileUtil.normalizeFile(prjRoot));
+          if (prjRootFO == null) {
+            continue;
           }
+          Project p = ProjectManager.getDefault().findProject(prjRootFO);
+          projectSet.add(p);
+        } catch (IOException ex) {
+          LOG.log(Level.WARNING, null, ex);
+        } catch (IllegalArgumentException ex) {
+          LOG.log(Level.WARNING, null, ex);
+        }
+      }
+    }
     return Collections.unmodifiableSet(projectSet);
   }
 

File project/src/org/netbeans/modules/android/project/PropertiesHelper.java

     String sdkDir = platformMgr.getSdkLocation();
     if (sdkDir != null &&
         !sdkDir.equals(localProps.getProperties().get(PropertyName.SDK_DIR.getName()))) {
-      try {
-        LOG.log(Level.CONFIG, "local.properties in {0} will be regenerated", 
-            project.getProjectDirectory().getPath());
-        
-        ProjectPropertiesWorkingCopy props = ProjectProperties.create(
-            project.getProjectDirectory().getPath(), ProjectProperties.PropertyType.LOCAL).makeWorkingCopy();
-        props.setProperty(PropertyName.SDK_DIR.getName(), sdkDir);
-        props.save();
-      } catch (IOException ex) {
-        Exceptions.printStackTrace(ex);
-        return false;
-      } catch (StreamException ex) {
-        Exceptions.printStackTrace(ex);
-        return false;
-      }
-      project.getProjectDirectory().refresh();
+      return setProperty(
+          ProjectProperties.PropertyType.LOCAL, PropertyName.SDK_DIR.getName(), sdkDir);
     }
     return true;
   }
+  
+  boolean setProperty(ProjectProperties.PropertyType type, String name, String value) {
+    try {
+      LOG.log(Level.CONFIG, "{1} in {0} will be regenerated", 
+          new Object[] {project.getProjectDirectory().getPath(), type});
+      
+      ProjectProperties loadedProps = ProjectProperties.load(
+          project.getProjectDirectory().getPath(), type);
+      ProjectPropertiesWorkingCopy props = loadedProps != null ? 
+          loadedProps.makeWorkingCopy() :
+          ProjectProperties.create(project.getProjectDirectory().getPath(), type);
+      if (value != null) {
+        props.setProperty(name, value);
+      } else {
+        props.removeProperty(name);
+      }
+      props.save();
+    } catch (IOException ex) {
+      Exceptions.printStackTrace(ex);
+      return false;
+    } catch (StreamException ex) {
+      Exceptions.printStackTrace(ex);
+      return false;
+    }
+    project.getProjectDirectory().refresh();
+    return true;
+    
+  }
 }

File project/src/org/netbeans/modules/android/project/queries/AndroidClassPath.java

    * A classpath used for given type in Android project.
    */
   ClassPath getClassPath(String type);
-  
-  /** Iterable of dependencies on project libraries. */
-  Iterable<File> getDependentProjectDirs();
 }

File project/src/org/netbeans/modules/android/project/queries/ClassPathProviderImpl.java

 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.project.ProjectManager;
 import org.netbeans.modules.android.core.sdk.DalvikPlatform;
 import org.netbeans.modules.android.project.AndroidProject;
-import org.netbeans.modules.android.project.AndroidProjectUtil;
 import org.netbeans.modules.android.project.PropertyName;
 import org.netbeans.modules.android.project.api.AndroidProjects;
 import org.netbeans.spi.java.classpath.ClassPathImplementation;
             Collections.singletonList(new SourcePathResources()));
     }
 
-  @Override
-  public Iterable<File> getDependentProjectDirs() {
-    List<File> prjDirs = new ArrayList<File>();
-    for (Map.Entry<String, String> prop : project.evaluator().getProperties().entrySet()) {
-      if (prop.getKey().startsWith("android.library.reference.")) {
-        File libPrjDir = new File(project.getProjectDirectoryFile(), prop.getValue());
-        prjDirs.add(libPrjDir);
-      }
-    }
-    return prjDirs;
-  }
-  
-  
   private Iterable<AndroidProject> getAllLibraryProjects() {
     List<AndroidProject> libraries = new ArrayList<AndroidProject>();
-    fillLibraryProjects(this, libraries);
+    fillLibraryProjects(project, libraries);
     return libraries;
   }
     
   
-  private void fillLibraryProjects(AndroidClassPath cp, List<AndroidProject> libraries) {
-    for (File libPrjDir : cp.getDependentProjectDirs()) {
+  private static void fillLibraryProjects(AndroidProject aPrj, List<AndroidProject> libraries) {
+    for (File libPrjDir : aPrj.info().getDependentProjectDirs()) {
       try {
         FileObject libPrjFO = FileUtil.toFileObject(FileUtil.normalizeFile(libPrjDir));
         if (libPrjFO == null) {
         if (ap == null) {
           continue;
         }
-        
         // look for more subprojects
-        AndroidClassPath libcp = p.getLookup().lookup(AndroidClassPath.class);
-        if (libcp != null) {
-          fillLibraryProjects(libcp, libraries);
-        }
+        fillLibraryProjects(ap, libraries);
         
         libraries.add(ap);
       }
         LOG.log(Level.WARNING, null, ex);
       }
     }
-    
     return;
   }
 

File project/src/org/netbeans/modules/android/project/ui/LibrariesNode.java

 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectManager;
 import org.netbeans.modules.android.project.AndroidProject;
+import org.netbeans.modules.android.project.AndroidProjectInfo;
 import org.netbeans.modules.android.project.queries.AndroidClassPath;
 import org.netbeans.spi.project.support.ant.PropertyEvaluator;
 import org.netbeans.spi.java.project.support.ui.PackageView;
         }
 
         private void addProjectLibs(List<Key> result) {
-          AndroidClassPath cpProvider = project.getLookup().lookup(AndroidClassPath.class);
-          if (cpProvider != null) {
-            for (File prjRoot : cpProvider.getDependentProjectDirs()) {
+          AndroidProjectInfo info = project.info();
+          if (info != null) {
+            for (File prjRoot : info.getDependentProjectDirs()) {
               try {
                 FileObject prjRootFO = FileUtil.toFileObject(FileUtil.normalizeFile(prjRoot));
                 if (prjRootFO == null) {

File project/src/org/netbeans/modules/android/project/ui/customizer/AndroidCompositePanelProvider.java

         AndroidGeneralData data = context.lookup(AndroidGeneralData.class);
         AndroidProjectInfo info = context.lookup(AndroidProjectInfo.class);
         if (LIBRARIES.equals(nm)) {
-            CustomizerProviderImpl.SubCategoryProvider prov = context.lookup(CustomizerProviderImpl.SubCategoryProvider.class);
-            assert prov != null : "Assuming CustomizerProviderImpl.SubCategoryProvider in customizer context";
-            return new CustomizerLibraries(project, prov);
+            return new CustomizerLibraries(data, project);
         } else if (GENERAL.equals(nm)) {
             return new CustomizerGeneral(data, info, DalvikPlatformManager.getDefault());
         } else if (RUN.equals(nm)) {

File project/src/org/netbeans/modules/android/project/ui/customizer/CustomizerLibraries.form

     <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
     <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
     <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
     <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
     <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
     <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
   </AuxValues>
 
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jSeparator1" alignment="0" pref="376" max="32767" attributes="0"/>
+                  <Component id="jCheckBoxIsLib" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Component id="jLabelReferrencedProjects" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Group type="102" alignment="1" attributes="0">
+                      <Component id="jScrollPane1" pref="302" max="32767" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" max="-2" attributes="0">
+                          <Component id="jButtonAddPrj" max="32767" attributes="1"/>
+                          <Component id="jButtonRemovePrj" alignment="0" max="32767" attributes="1"/>
+                      </Group>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jCheckBoxIsLib" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jLabelReferrencedProjects" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" attributes="0">
+                      <Component id="jButtonAddPrj" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="jButtonRemovePrj" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Component id="jScrollPane1" min="-2" pref="174" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
   <SubComponents>
-    <Component class="javax.swing.JLabel" name="jLabelTarget">
+    <Component class="javax.swing.JCheckBox" name="jCheckBoxIsLib">
       <Properties>
-        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
-          <ComponentRef name="jComboBoxTarget"/>
-        </Property>
-        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="org/netbeans/modules/android/project/ui/customizer/Bundle.properties" key="LBL_CustomizeGeneral_Platform_JLabel" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-        </Property>
+        <Property name="text" type="java.lang.String" value="Is library"/>
       </Properties>
-      <AccessibilityProperties>
-        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="org/netbeans/modules/android/project/ui/customizer/Bundle.properties" key="ACSD_CustomizerGeneral_jLabelTarget" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-        </Property>
-      </AccessibilityProperties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jCheckBoxIsLibActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JSeparator" name="jSeparator1">
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabelReferrencedProjects">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Referrenced library projects:"/>
+      </Properties>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
       <AuxValues>
-        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
       </AuxValues>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="12" insetsRight="12" anchor="17" weightX="0.0" weightY="0.0"/>
-        </Constraint>
-      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTable" name="jTableLibs">
+          <Properties>
+            <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="libsTableModel" type="code"/>
+            </Property>
+            <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
+              <TableColumnModel selectionModel="1"/>
+            </Property>
+            <Property name="columnSelectionAllowed" type="boolean" value="true"/>
+            <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
+              <TableHeader reorderingAllowed="true" resizingAllowed="true"/>
+            </Property>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JButton" name="jButtonAddPrj">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Add"/>
+      </Properties>
     </Component>
-    <Component class="javax.swing.JComboBox" name="jComboBoxTarget">
+    <Component class="javax.swing.JButton" name="jButtonRemovePrj">
       <Properties>
-        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-          <Connection code="this.jComboBoxTarget.getPreferredSize()" type="code"/>
-        </Property>
+        <Property name="text" type="java.lang.String" value="Remove"/>
       </Properties>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="12" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>
-        </Constraint>
-      </Constraints>
-    </Component>
-    <Component class="javax.swing.JButton" name="jButton1">
-      <Properties>
-        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="org/netbeans/modules/android/project/ui/customizer/Bundle.properties" key="LBL_CustomizeGeneral_Platform_JButton" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-        </Property>
-      </Properties>
-      <AccessibilityProperties>
-        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="org/netbeans/modules/android/project/ui/customizer/Bundle.properties" key="ACSD_CustomizerGeneral_jButton1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-        </Property>
-      </AccessibilityProperties>
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="createNewPlatform"/>
-      </Events>
-      <AuxValues>
-        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
-      </AuxValues>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="0" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="12" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
-        </Constraint>
-      </Constraints>
     </Component>
   </SubComponents>
 </Form>

File project/src/org/netbeans/modules/android/project/ui/customizer/CustomizerLibraries.java

 
 package org.netbeans.modules.android.project.ui.customizer;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import javax.swing.JPanel;
-import javax.swing.event.ListDataEvent;
-import javax.swing.event.ListDataListener;
+import javax.swing.table.DefaultTableModel;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectInformation;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.modules.android.project.AndroidGeneralData;
 import org.netbeans.modules.android.project.AndroidProject;
-import org.openide.util.HelpCtx;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
 
-/** Customizer for general project attributes.
- *
+/** 
+ * Customizer for Android libraries.
  */
-public class CustomizerLibraries extends JPanel implements HelpCtx.Provider, ListDataListener {
+public class CustomizerLibraries extends JPanel {
+  private static final Logger LOG = Logger.getLogger(CustomizerLibraries.class.getName());
     
-    public static final String COMPILE = "COMPILE";  //NOI18N
-    public static final String RUN = "RUN";          //NOI18N
-    public static final String COMPILE_TESTS = "COMPILE_TESTS"; //NOI18N
-    public final String RUN_TESTS = "RUN_TESTS";  //NOI18N        
-    
-    private final AndroidProject project;    
-    
-    public CustomizerLibraries(AndroidProject project, CustomizerProviderImpl.SubCategoryProvider subcat) {
-        this.project = project;
-        initComponents();        
+  private final AndroidGeneralData data;
+  private final AndroidProject project;
+  private final DefaultTableModel libsTableModel;
+
+  public CustomizerLibraries(AndroidGeneralData data, AndroidProject project) {
+    this.data = data;
+    this.project = project;
+    libsTableModel = new javax.swing.table.DefaultTableModel(
+        new Object [][] {},
+        new String [] {"Name", "Directory"}) {
+      Class[] types = new Class [] {
+        java.lang.String.class, java.lang.String.class
+      };
+      boolean[] canEdit = new boolean [] {
+        false, false
+      };
+
+      @Override
+      public Class getColumnClass(int columnIndex) {
+        return types[columnIndex];
+      }
+
+      @Override
+      public boolean isCellEditable(int rowIndex, int columnIndex) {
+        return canEdit[columnIndex];
+      }
+    };
+    initComponents();        
+    initFromProject(project);
+  }
         
-        this.putClientProperty( "HelpID", "J2SE_CustomizerGeneral" ); // NOI18N
-
-        
-//        jComboBoxTarget.setModel(uiProperties.PLATFORM_MODEL);               
-//        jComboBoxTarget.setRenderer(uiProperties.PLATFORM_LIST_RENDERER);
-        jComboBoxTarget.putClientProperty ("JComboBox.isTableCellEditor", Boolean.TRUE);    //NOI18N
-        jComboBoxTarget.addItemListener(new java.awt.event.ItemListener(){ 
-            public void itemStateChanged(java.awt.event.ItemEvent e){ 
-                javax.swing.JComboBox combo = (javax.swing.JComboBox)e.getSource(); 
-                combo.setPopupVisible(false); 
-            } 
-        });
-        if (AndroidCompositePanelProvider.LIBRARIES.equals(subcat.getCategory())) {
-            // XXX
-        }
-    }
-        
-    // Implementation of HelpCtx.Provider --------------------------------------
-    
-    public HelpCtx getHelpCtx() {
-        return new HelpCtx( CustomizerLibraries.class );
-    }        
-
-    
-    // Implementation of ListDataListener --------------------------------------
-    
-    
-    public void intervalRemoved( ListDataEvent e ) {
-    }
-
-    public void intervalAdded( ListDataEvent e ) {
-        // NOP
-    }
-
-    public void contentsChanged( ListDataEvent e ) {
-        // NOP
-    }
-    
-    
     /** This method is called from within the constructor to
      * initialize the form.
      * WARNING: Do NOT modify this code. The content of this method is
      * always regenerated by the Form Editor.
      */
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-        java.awt.GridBagConstraints gridBagConstraints;
+  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+  private void initComponents() {
 
-        jLabelTarget = new javax.swing.JLabel();
-        jComboBoxTarget = new javax.swing.JComboBox();
-        jButton1 = new javax.swing.JButton();
+    jCheckBoxIsLib = new javax.swing.JCheckBox();
+    jSeparator1 = new javax.swing.JSeparator();
+    jLabelReferrencedProjects = new javax.swing.JLabel();
+    jScrollPane1 = new javax.swing.JScrollPane();
+    jTableLibs = new javax.swing.JTable();
+    jButtonAddPrj = new javax.swing.JButton();
+    jButtonRemovePrj = new javax.swing.JButton();
 
-        setLayout(new java.awt.GridBagLayout());
+    org.openide.awt.Mnemonics.setLocalizedText(jCheckBoxIsLib, "Is library");
+    jCheckBoxIsLib.addActionListener(new java.awt.event.ActionListener() {
+      public void actionPerformed(java.awt.event.ActionEvent evt) {
+        jCheckBoxIsLibActionPerformed(evt);
+      }
+    });
 
-        jLabelTarget.setLabelFor(jComboBoxTarget);
-        org.openide.awt.Mnemonics.setLocalizedText(jLabelTarget, org.openide.util.NbBundle.getMessage(CustomizerLibraries.class, "LBL_CustomizeGeneral_Platform_JLabel")); // NOI18N
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 12);
-        add(jLabelTarget, gridBagConstraints);
-        jLabelTarget.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CustomizerLibraries.class, "ACSD_CustomizerGeneral_jLabelTarget")); // NOI18N
+    org.openide.awt.Mnemonics.setLocalizedText(jLabelReferrencedProjects, "Referrenced library projects:");
 
-        jComboBoxTarget.setMinimumSize(this.jComboBoxTarget.getPreferredSize());
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 0);
-        add(jComboBoxTarget, gridBagConstraints);
+    jTableLibs.setModel(libsTableModel);
+    jTableLibs.setColumnSelectionAllowed(true);
+    jScrollPane1.setViewportView(jTableLibs);
+    jTableLibs.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
 
-        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(CustomizerLibraries.class, "LBL_CustomizeGeneral_Platform_JButton")); // NOI18N
-        jButton1.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                createNewPlatform(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 0);
-        add(jButton1, gridBagConstraints);
-        jButton1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CustomizerLibraries.class, "ACSD_CustomizerGeneral_jButton1")); // NOI18N
-    }// </editor-fold>//GEN-END:initComponents
+    org.openide.awt.Mnemonics.setLocalizedText(jButtonAddPrj, "Add");
 
-    private void createNewPlatform(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createNewPlatform
-        Object selectedItem = this.jComboBoxTarget.getSelectedItem();
-        // XXX
-    }//GEN-LAST:event_createNewPlatform
+    org.openide.awt.Mnemonics.setLocalizedText(jButtonRemovePrj, "Remove");
+
+    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+    this.setLayout(layout);
+    layout.setHorizontalGroup(
+      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+      .addGroup(layout.createSequentialGroup()
+        .addContainerGap()
+        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+          .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
+          .addComponent(jCheckBoxIsLib)
+          .addComponent(jLabelReferrencedProjects)
+          .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 302, Short.MAX_VALUE)
+            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+              .addComponent(jButtonAddPrj, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+              .addComponent(jButtonRemovePrj, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+        .addContainerGap())
+    );
+    layout.setVerticalGroup(
+      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+      .addGroup(layout.createSequentialGroup()
+        .addContainerGap()
+        .addComponent(jCheckBoxIsLib)
+        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+        .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+        .addComponent(jLabelReferrencedProjects)
+        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+          .addGroup(layout.createSequentialGroup()
+            .addComponent(jButtonAddPrj)
+            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+            .addComponent(jButtonRemovePrj))
+          .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE))
+        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+    );
+  }// </editor-fold>//GEN-END:initComponents
+
+  private void jCheckBoxIsLibActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxIsLibActionPerformed
+    updateData();
+  }//GEN-LAST:event_jCheckBoxIsLibActionPerformed
     
-    
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JButton jButton1;
-    private javax.swing.JComboBox jComboBoxTarget;
-    private javax.swing.JLabel jLabelTarget;
-    // End of variables declaration//GEN-END:variables
+  // Variables declaration - do not modify//GEN-BEGIN:variables
+  private javax.swing.JButton jButtonAddPrj;
+  private javax.swing.JButton jButtonRemovePrj;
+  private javax.swing.JCheckBox jCheckBoxIsLib;
+  private javax.swing.JLabel jLabelReferrencedProjects;
+  private javax.swing.JScrollPane jScrollPane1;
+  private javax.swing.JSeparator jSeparator1;
+  private javax.swing.JTable jTableLibs;
+  // End of variables declaration//GEN-END:variables
         
-        
+  private void initFromProject(AndroidProject project) {
+    jCheckBoxIsLib.setSelected(project.info().isLibrary());
+    for (File lib : project.info().getDependentProjectDirs()) {
+      FileObject libFo = FileUtil.toFileObject(FileUtil.normalizeFile(lib));
+      Project libPrj = null;
+      try {
+        libPrj = libFo != null ? ProjectManager.getDefault().findProject(libFo) : null;
+      } catch (IOException ex) {
+        LOG.log(Level.INFO, null, ex);
+      } catch (IllegalArgumentException ex) {
+        LOG.log(Level.INFO, null, ex);
+      }
+      String name = libPrj != null ? 
+          libPrj.getLookup().lookup(ProjectInformation.class).getDisplayName() : 
+          lib.getName();
+      String path = lib.getPath();
+      libsTableModel.addRow(new Object[] {name, path});
+    }
+  }
+
+  private void updateData() {
+    data.setLibrary(jCheckBoxIsLib.isSelected());
+  }
 }

File project/src/org/netbeans/modules/android/project/ui/customizer/CustomizerRun.java

 package org.netbeans.modules.android.project.ui.customizer;
 
 import com.android.sdklib.xml.ManifestData;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
 import javax.swing.JPanel;
 import org.netbeans.modules.android.project.AndroidProject;
 import org.netbeans.modules.android.project.AndroidProjectUtil;
 import org.netbeans.modules.android.project.launch.LaunchConfiguration;
-import org.netbeans.modules.android.project.launch.Launches;
 import org.openide.util.HelpCtx;
 
 public class CustomizerRun extends JPanel implements HelpCtx.Provider {
     
-  private AndroidProject project;
   private LaunchConfiguration launchConfig;
     
-  public CustomizerRun(
-      AndroidProject project, LaunchConfiguration launchConfig) {
-    this.project = project;
+  public CustomizerRun(AndroidProject project, LaunchConfiguration launchConfig) {
     this.launchConfig = launchConfig;
     initComponents();
     initFromProject(project);

File project/src/org/netbeans/modules/android/project/ui/resources/layer.xml

                     <attr name="instanceCreate" methodvalue="org.netbeans.modules.android.project.ui.customizer.AndroidCompositePanelProvider.createGeneral"/>
                     <attr name="position" intvalue="100"/>
                 </file>
-                <!--
                 <file name="Libraries.instance">
                     <attr name="instanceCreate" methodvalue="org.netbeans.modules.android.project.ui.customizer.AndroidCompositePanelProvider.createLibraries"/>
                     <attr name="position" intvalue="200"/>
                 </file>
+                <!--
                 <folder name="BuildCategory">
                     <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.android.project.ui.customizer.Bundle"/>
                     <attr name="position" intvalue="300"/>

File project/test/unit/src/org/netbeans/modules/android/project/AndroidInfoImplTest.java

   private static File tempFolder;
   private static FileObject projdir;
   private static FileObject testprojdir;
+  private static FileObject mainprojdir;
+  private static FileObject libprojdir;
 
   private static Project srcPrj;
   private static Project testPrj;
+  private static Project mainPrj;
+  private static Project libPrj;
   private static FileObject someSource1;
   private static FileObject someTest1;
     
     someSource1 = projdir.getFileObject("src/com/android/example/spinner/SpinnerActivity.java");
     testPrj = ProjectManager.getDefault().findProject(testprojdir);
     someTest1 = testprojdir.getFileObject("src/com/android/example/spinner/test/SpinnerActivityTest.java");
-    }
+    
+    mainprojdir = scratch.createFolder("TicTacToeMain");
+    FileUtilities.recursiveCopy(sdkDirFo.getFileObject("samples/android-8/TicTacToeMain"), mainprojdir);
+    libprojdir = scratch.createFolder("TicTacToeLib");
+    FileUtilities.recursiveCopy(sdkDirFo.getFileObject("samples/android-8/TicTacToeLib"), libprojdir);
 
+    mainPrj = ProjectManager.getDefault().findProject(mainprojdir);
+    libPrj = ProjectManager.getDefault().findProject(libprojdir);
+  }
 
-  
   @AfterClass
   public static void delete() {
-    FileUtilities.recursiveDelete(tempFolder);
+    System.err.println("leaving " + tempFolder.getAbsolutePath());
+//    FileUtilities.recursiveDelete(tempFolder);
   }
   
   @Test
   }
 
   @Test
+  public void isLibrary() throws Exception {
+    AndroidProject proj = (AndroidProject) ProjectManager.getDefault().findProject(libprojdir);
+    AndroidGeneralData data = AndroidGeneralData.fromProject(proj);
+    data.setPlatform(DalvikPlatformManager.getDefault().findPlatformForTarget("android-8"));
+    proj.update(data);
+    AndroidProjectInfo aInfo = proj.info();
+    assertTrue(aInfo.isLibrary());
+    assertFalse(aInfo.isTest());
+    
+    // Turn off library flag.
+    data = AndroidGeneralData.fromProject(proj);
+    data.setPlatform(DalvikPlatformManager.getDefault().findPlatformForTarget("android-8"));
+    data.setLibrary(false);
+    proj.update(data);
+    aInfo = proj.info();
+    assertFalse(proj + " is not library", aInfo.isLibrary());
+    
+    // Turn on library flag.
+    data = AndroidGeneralData.fromProject(proj);
+    data.setPlatform(DalvikPlatformManager.getDefault().findPlatformForTarget("android-8"));
+    data.setLibrary(true);
+    proj.update(data);
+    aInfo = proj.info();
+    assertTrue(proj + " is not library", aInfo.isLibrary());
+  }
+
+  @Test
   public void newTestProject() throws Exception {
     AndroidGeneralData data = new AndroidGeneralData();
     File prjDir = new File(tempFolder, "project");