Example #1
0
  /**
   * Returns all activities found in the given project (including those in libraries, except for
   * android.jar itself)
   *
   * @param project the project
   * @return a list of activity classes as fully qualified class names
   */
  @SuppressWarnings("restriction") // BinaryType
  @NonNull
  public static List<String> getProjectActivities(IProject project) {
    final List<String> activities = new ArrayList<String>();
    try {
      final IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
      if (javaProject != null) {
        IType[] activityTypes = new IType[0];
        IType activityType = javaProject.findType(SdkConstants.CLASS_ACTIVITY);
        if (activityType != null) {
          ITypeHierarchy hierarchy =
              activityType.newTypeHierarchy(javaProject, new NullProgressMonitor());
          activityTypes = hierarchy.getAllSubtypes(activityType);
          for (IType type : activityTypes) {
            if (type instanceof BinaryType
                && (type.getClassFile() == null || type.getClassFile().getResource() == null)) {
              continue;
            }
            activities.add(type.getFullyQualifiedName());
          }
        }
      }
    } catch (CoreException e) {
      AdtPlugin.log(e, null);
    }

    return activities;
  }
  private IProject guessProject(IStructuredSelection selection) {
    if (selection == null) {
      return null;
    }

    for (Object element : selection.toList()) {
      if (element instanceof IAdaptable) {
        IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class);
        IProject project = res != null ? res.getProject() : null;

        // Is this an Android project?
        try {
          if (project == null || !project.hasNature(AdtConstants.NATURE_DEFAULT)) {
            continue;
          }
        } catch (CoreException e) {
          // checking the nature failed, ignore this resource
          continue;
        }

        return project;
      } else if (element instanceof Pair<?, ?>) {
        // Pair of Project/String
        @SuppressWarnings("unchecked")
        Pair<IProject, String> pair = (Pair<IProject, String>) element;
        return pair.getFirst();
      }
    }

    // Try to figure out the project from the active editor
    IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    if (window != null) {
      IWorkbenchPage page = window.getActivePage();
      if (page != null) {
        IEditorPart activeEditor = page.getActiveEditor();
        if (activeEditor instanceof AndroidXmlEditor) {
          Object input = ((AndroidXmlEditor) activeEditor).getEditorInput();
          if (input instanceof FileEditorInput) {
            FileEditorInput fileInput = (FileEditorInput) input;
            return fileInput.getFile().getProject();
          }
        }
      }
    }

    IJavaProject[] projects =
        BaseProjectHelper.getAndroidProjects(
            new IProjectFilter() {
              public boolean accept(IProject project) {
                return project.isAccessible();
              }
            });

    if (projects != null && projects.length == 1) {
      return projects[0].getProject();
    }

    return null;
  }
Example #3
0
  /**
   * Returns the activities associated with the given layout file. Makes an educated guess by
   * peeking at the usages of the R.layout.name field corresponding to the layout and if it finds a
   * usage.
   *
   * @param project the project containing the layout
   * @param layoutName the layout whose activity we want to look up
   * @param pkg the package containing activities
   * @return the activity name
   */
  @NonNull
  public static List<String> guessActivities(IProject project, String layoutName, String pkg) {
    final LinkedList<String> activities = new LinkedList<String>();
    SearchRequestor requestor =
        new SearchRequestor() {
          @Override
          public void acceptSearchMatch(SearchMatch match) throws CoreException {
            Object element = match.getElement();
            if (element instanceof IMethod) {
              IMethod method = (IMethod) element;
              IType declaringType = method.getDeclaringType();
              String fqcn = declaringType.getFullyQualifiedName();

              if ((declaringType.getSuperclassName() != null
                      && declaringType.getSuperclassName().endsWith("Activity")) // $NON-NLS-1$
                  || method.getElementName().equals("onCreate")) { // $NON-NLS-1$
                activities.addFirst(fqcn);
              } else {
                activities.addLast(fqcn);
              }
            }
          }
        };
    try {
      IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
      if (javaProject == null) {
        return Collections.emptyList();
      }
      // TODO - look around a bit more and see if we can figure out whether the
      // call if from within a setContentView call!

      // Search for which java classes call setContentView(R.layout.layoutname);
      String typeFqcn = "R.layout"; // $NON-NLS-1$
      if (pkg != null) {
        typeFqcn = pkg + '.' + typeFqcn;
      }

      IType type = javaProject.findType(typeFqcn);
      if (type != null) {
        IField field = type.getField(layoutName);
        if (field.exists()) {
          SearchPattern pattern = SearchPattern.createPattern(field, REFERENCES);
          try {
            search(requestor, javaProject, pattern);
          } catch (OperationCanceledException canceled) {
            // pass
          }
        }
      }
    } catch (CoreException e) {
      AdtPlugin.log(e, null);
    }

    return activities;
  }
Example #4
0
  /**
   * Returns the first package root for the given java project
   *
   * @param javaProject the project to search in
   * @return the first package root, or null
   */
  @Nullable
  public static IPackageFragmentRoot getSourcePackageRoot(IJavaProject javaProject) {
    IPackageFragmentRoot packageRoot = null;
    List<IPath> sources = BaseProjectHelper.getSourceClasspaths(javaProject);

    IWorkspace workspace = ResourcesPlugin.getWorkspace();
    for (IPath path : sources) {
      IResource firstSource = workspace.getRoot().findMember(path);
      if (firstSource != null) {
        packageRoot = javaProject.getPackageFragmentRoot(firstSource);
        if (packageRoot != null) {
          break;
        }
      }
    }
    return packageRoot;
  }
Example #5
0
  /**
   * Returns the {@link IPackageFragment} for the package registered in the manifest
   *
   * @return the {@link IPackageFragment} for the package registered in the manifest
   */
  @Nullable
  public IPackageFragment getPackageFragment() {
    sync();
    try {
      IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject);
      if (javaProject != null) {
        IPackageFragmentRoot root = ManifestInfo.getSourcePackageRoot(javaProject);
        if (root != null) {
          return root.getPackageFragment(mPackage);
        }
      }
    } catch (CoreException e) {
      AdtPlugin.log(e, null);
    }

    return null;
  }
Example #6
0
  /**
   * Returns the activity associated with the given layout file.
   *
   * <p>This is an alternative to {@link #guessActivity(IFile, String)}. Whereas guessActivity
   * simply looks for references to "R.layout.foo", this method searches for all usages of
   * Activity#setContentView(int), and for each match it looks up the corresponding call text (such
   * as "setContentView(R.layout.foo)"). From this it uses a regexp to pull out "foo" from this, and
   * stores the association that layout "foo" is associated with the activity class that contained
   * the setContentView call.
   *
   * <p>This has two potential advantages:
   *
   * <ol>
   *   <li>It can be faster. We do the reference search -once-, and we've built a map of all the
   *       layout-to-activity mappings which we can then immediately look up other layouts for,
   *       which is particularly useful at startup when we have to compute the layout activity
   *       associations to populate the theme choosers.
   *   <li>It can be more accurate. Just because an activity references an "R.layout.foo" field
   *       doesn't mean it's setting it as a content view.
   * </ol>
   *
   * However, this second advantage is also its chief problem. There are some common code constructs
   * which means that the associated layout is not explicitly referenced in a direct setContentView
   * call; on a couple of sample projects I tested I found patterns like for example
   * "setContentView(v)" where "v" had been computed earlier. Therefore, for now we're going to
   * stick with the more general approach of just looking up each field when needed. We're keeping
   * the code around, though statically compiled out with the "if (false)" construct below in case
   * we revisit this.
   *
   * @param layoutFile the layout whose activity we want to look up
   * @return the activity name
   */
  @SuppressWarnings("all")
  @Nullable
  public String guessActivityBySetContentView(String layoutName) {
    if (false) {
      // These should be fields
      final Pattern LAYOUT_FIELD_PATTERN =
          Pattern.compile("R\\.layout\\.([a-z0-9_]+)"); // $NON-NLS-1$
      Map<String, String> mUsages = null;

      sync();
      if (mUsages == null) {
        final Map<String, String> usages = new HashMap<String, String>();
        mUsages = usages;
        SearchRequestor requestor =
            new SearchRequestor() {
              @Override
              public void acceptSearchMatch(SearchMatch match) throws CoreException {
                Object element = match.getElement();
                if (element instanceof IMethod) {
                  IMethod method = (IMethod) element;
                  IType declaringType = method.getDeclaringType();
                  String fqcn = declaringType.getFullyQualifiedName();
                  IDocumentProvider provider = new TextFileDocumentProvider();
                  IResource resource = match.getResource();
                  try {
                    provider.connect(resource);
                    IDocument document = provider.getDocument(resource);
                    if (document != null) {
                      String matchText = document.get(match.getOffset(), match.getLength());
                      Matcher matcher = LAYOUT_FIELD_PATTERN.matcher(matchText);
                      if (matcher.find()) {
                        usages.put(matcher.group(1), fqcn);
                      }
                    }
                  } catch (Exception e) {
                    AdtPlugin.log(e, "Can't find range information for %1$s", resource.getName());
                  } finally {
                    provider.disconnect(resource);
                  }
                }
              }
            };
        try {
          IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject);
          if (javaProject == null) {
            return null;
          }

          // Search for which java classes call setContentView(R.layout.layoutname);
          String typeFqcn = "R.layout"; // $NON-NLS-1$
          if (mPackage != null) {
            typeFqcn = mPackage + '.' + typeFqcn;
          }

          IType activityType = javaProject.findType(SdkConstants.CLASS_ACTIVITY);
          if (activityType != null) {
            IMethod method =
                activityType.getMethod(
                    "setContentView", new String[] {"I"}); // $NON-NLS-1$ //$NON-NLS-2$
            if (method.exists()) {
              SearchPattern pattern = SearchPattern.createPattern(method, REFERENCES);
              search(requestor, javaProject, pattern);
            }
          }
        } catch (CoreException e) {
          AdtPlugin.log(e, null);
        }
      }

      return mUsages.get(layoutName);
    }

    return null;
  }
  private void buildErrorUi(IProject project) {
    // Show description the first time
    setErrorMessage(null);
    setMessage(null);
    setPageComplete(true);
    mHasMessage = false;

    // composite parent for the warning/error
    GridLayout gl = null;
    mErrorComposite = new Composite(mTopComposite, SWT.NONE);
    mErrorComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    gl = new GridLayout(2, false);
    gl.marginHeight = gl.marginWidth = 0;
    gl.verticalSpacing *= 3; // more spacing than normal.
    mErrorComposite.setLayout(gl);

    if (project == null) {
      setErrorMessage("Select project to export.");
      mHasMessage = true;
    } else {
      try {
        if (project.hasNature(AndroidConstants.NATURE) == false) {
          addError(mErrorComposite, "Project is not an Android project.");
        } else {
          // check for errors
          if (ProjectHelper.hasError(project, true)) {
            addError(mErrorComposite, "Project has compilation error(s)");
          }

          // check the project output
          IFolder outputIFolder = BaseProjectHelper.getOutputFolder(project);
          if (outputIFolder != null) {
            String outputOsPath = outputIFolder.getLocation().toOSString();
            String apkFilePath =
                outputOsPath
                    + File.separator
                    + project.getName()
                    + AndroidConstants.DOT_ANDROID_PACKAGE;

            File f = new File(apkFilePath);
            if (f.isFile() == false) {
              addError(
                  mErrorComposite,
                  String.format(
                      "%1$s/%2$s/%1$s%3$s does not exists!",
                      project.getName(),
                      outputIFolder.getName(),
                      AndroidConstants.DOT_ANDROID_PACKAGE));
            }
          } else {
            addError(mErrorComposite, "Unable to get the output folder of the project!");
          }

          // project is an android project, we check the debuggable attribute.
          AndroidManifestParser manifestParser =
              AndroidManifestParser.parse(
                  BaseProjectHelper.getJavaProject(project),
                  null /* errorListener */,
                  true /* gatherData */,
                  false /* markErrors */);

          Boolean debuggable = manifestParser.getDebuggable();

          if (debuggable != null && debuggable == Boolean.TRUE) {
            addWarning(
                mErrorComposite,
                "The manifest 'debuggable' attribute is set to true.\nYou should set it to false for applications that you release to the public.");
          }

          // check for mapview stuff
        }
      } catch (CoreException e) {
        // unable to access nature
        addError(mErrorComposite, "Unable to get project nature");
      }
    }

    if (mHasMessage == false) {
      Label label = new Label(mErrorComposite, SWT.NONE);
      GridData gd = new GridData(GridData.FILL_HORIZONTAL);
      gd.horizontalSpan = 2;
      label.setLayoutData(gd);
      label.setText("No errors found. Click Next.");
    }

    mTopComposite.layout();
  }
  private void selectFiles(IProject project, List<? extends IResource> createdFiles) {
    // Attempt to select the newly created files in the Package Explorer
    IWorkbench workbench = AdtPlugin.getDefault().getWorkbench();
    IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage();
    IViewPart viewPart = page.findView(JavaUI.ID_PACKAGES);
    if (viewPart != null) {
      IWorkbenchPartSite site = viewPart.getSite();
      IJavaProject javaProject = null;
      try {
        javaProject = BaseProjectHelper.getJavaProject(project);
      } catch (CoreException e) {
        AdtPlugin.log(e, null);
      }
      final ISelectionProvider provider = site.getSelectionProvider();
      if (provider != null) {
        List<TreePath> pathList = new ArrayList<TreePath>();
        for (IResource file : createdFiles) {
          // Create a TreePath for the given file,
          // which should be the JavaProject, followed by the folders down to
          // the final file.
          List<Object> segments = new ArrayList<Object>();
          segments.add(file);
          IContainer folder = file.getParent();
          if (folder != null && !(folder instanceof IProject)) {
            segments.add(folder);
            // res folder
            folder = folder.getParent();
            if (folder != null && !(folder instanceof IProject)) {
              segments.add(folder);
            }
          }
          // project
          segments.add(javaProject);

          Collections.reverse(segments);
          TreePath path = new TreePath(segments.toArray());
          pathList.add(path);

          // IDEA: Maybe normalize the files backwards (IFile objects aren't unique)
          // by maybe using the package explorer icons instead
        }

        TreePath[] paths = pathList.toArray(new TreePath[pathList.size()]);
        final TreeSelection selection = new TreeSelection(paths);

        provider.setSelection(selection);

        // Workaround: The above doesn't always work; it will frequently select
        // some siblings of the real files. I've tried a number of workarounds:
        // normalizing the IFile objects by looking up the canonical ones via
        // their relative paths from the project; deferring execution with
        // Display.asyncRun; first calling select on the parents, etc.
        // However, it turns out a simple workaround works best: Calling this
        // method TWICE. The first call seems to expand all the necessary parents,
        // and the second call ensures that the correct children are selected!
        provider.setSelection(selection);

        viewPart.setFocus();
      }
    }
  }
Example #9
0
  private Pair<List<String>, List<String>> findViews(final boolean layoutsOnly) {
    final Set<String> customViews = new HashSet<String>();
    final Set<String> thirdPartyViews = new HashSet<String>();

    ProjectState state = Sdk.getProjectState(mProject);
    final List<IProject> libraries =
        state != null ? state.getFullLibraryProjects() : Collections.<IProject>emptyList();

    SearchRequestor requestor =
        new SearchRequestor() {
          @Override
          public void acceptSearchMatch(SearchMatch match) throws CoreException {
            // Ignore matches in comments
            if (match.isInsideDocComment()) {
              return;
            }

            Object element = match.getElement();
            if (element instanceof ResolvedBinaryType) {
              // Third party view
              ResolvedBinaryType type = (ResolvedBinaryType) element;
              IPackageFragment fragment = type.getPackageFragment();
              IPath path = fragment.getPath();
              String last = path.lastSegment();
              // Filter out android.jar stuff
              if (last.equals(FN_FRAMEWORK_LIBRARY)) {
                return;
              }
              if (!isValidView(type, layoutsOnly)) {
                return;
              }

              IProject matchProject = match.getResource().getProject();
              if (mProject == matchProject || libraries.contains(matchProject)) {
                String fqn = type.getFullyQualifiedName();
                thirdPartyViews.add(fqn);
              }
            } else if (element instanceof ResolvedSourceType) {
              // User custom view
              IProject matchProject = match.getResource().getProject();
              if (mProject == matchProject || libraries.contains(matchProject)) {
                ResolvedSourceType type = (ResolvedSourceType) element;
                if (!isValidView(type, layoutsOnly)) {
                  return;
                }
                String fqn = type.getFullyQualifiedName();
                fqn = fqn.replace('$', '.');
                customViews.add(fqn);
              }
            }
          }
        };
    try {
      IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject);
      if (javaProject != null) {
        String className = layoutsOnly ? CLASS_VIEWGROUP : CLASS_VIEW;
        IType viewType = javaProject.findType(className);
        if (viewType != null) {
          IJavaSearchScope scope = SearchEngine.createHierarchyScope(viewType);
          SearchParticipant[] participants =
              new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()};
          int matchRule = SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE;

          SearchPattern pattern =
              SearchPattern.createPattern(
                  "*", IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS, matchRule);
          SearchEngine engine = new SearchEngine();
          engine.search(pattern, participants, scope, requestor, new NullProgressMonitor());
        }
      }
    } catch (CoreException e) {
      AdtPlugin.log(e, null);
    }

    List<String> custom = new ArrayList<String>(customViews);
    List<String> thirdParty = new ArrayList<String>(thirdPartyViews);

    if (!layoutsOnly) {
      // Update our cached answers (unless we were filtered on only layouts)
      mCustomViews = custom;
      mThirdPartyViews = thirdParty;
    }

    return Pair.of(custom, thirdParty);
  }