sockeqwe / AnnotatedAdapter

Write less code with AnnotatedAdapter, an annotation processor for generating RecyclerView and AbsListView adapters
http://hannesdorfmann.com/android/AnnotatedAdapter/
Apache License 2.0
195 stars 31 forks source link

Automatically detect ViewHolder fields #4

Open sockeqwe opened 10 years ago

sockeqwe commented 10 years ago

Automatically detect fields from the corresponding xml layout files by checking for views with an id. So you don't have to specify fields for the view holder by hand with@Field

However, that's not an easy task:

  1. How do I access resource folder from the annotation processor? It runs in it's own jvm. How do I get the path of the current execution
  2. Layout xml files in different folders like layout, xlarge-layout etc.
  3. The res folder itself is configureable through gradle configuration
  4. xml layout can include other layouts

However it would be awesome to have such a feature!

sockeqwe commented 10 years ago

Tried to get the path to the folder containing the project with:

 private String getExecutionPath() throws UnsupportedEncodingException {
    String path = AnnotatedAdapterProcessor.class.getProtectionDomain()
        .getCodeSource()
        .getLocation()
        .getPath();
    String decodedPath = URLDecoder.decode(path, "UTF-8");

    return decodedPath;
  }

  private String getWorkingDir() {
    Path currentRelativePath = Paths.get("");
    String s = currentRelativePath.toAbsolutePath().toString();
    return s;
  }

  private String getExcecutionByClassLoader() {
    ClassLoader loader = AnnotatedAdapterProcessor.class.getClassLoader();
    URL url = loader.getResource(".");
    return url.getFile();
  }

  private String getByEnv() {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);

    Iterable<? extends File> locations = fm.getLocation(StandardLocation.SOURCE_PATH);
    if (locations.iterator().hasNext()) {
      return locations.iterator().next().getAbsolutePath();
    }
    return null;
  }

None of this method returns the desired result

fcolasuonno commented 10 years ago

You could pass an argument to the apt processor: By adding the following to the build.gradle:

apt {
    arguments {
        androidManifestFile variant.processResources.manifestFile
    }
}

the manifest file location will be passed as argument to the processor, then you can retrieve the layouts like this:

final String androidManifestFile = processingEnv.getOptions().get("androidManifestFile");
int index = androidManifestFile.lastIndexOf("manifests");
File androidManifest = new File(androidManifestFile);
String variant = androidManifest.getParentFile().getName();
File layoutsDir = new File(androidManifestFile.substring(0,index),"res/"+variant+"/layout");

for (File file : layoutsDir.listFiles()) {
     processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,file.getAbsolutePath());
}

(error checking omitted)

sockeqwe commented 10 years ago

Thanks, good idea! That looks really promising!

android {
 sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            res.srcDirs = ['res']
        }
    }
}

I guess that the path to res folder can be passed directly as annotation processor option somehow ...

fcolasuonno commented 10 years ago

According to http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Manipulating-tasks you could get the resource dir by using

apt {
    arguments {
        androidManifestFile variant.processResources.resDir
    }
}

But I don't know if it's valid in case multiple resource dirs are specified in the sourceSets

sockeqwe commented 10 years ago

Awesome! Thanks! I will try that tomorrow!

sockeqwe commented 10 years ago

But what if you want to use AnnotatedAdapter within a android library project? The layoutfiles will be somewhere in build/generated/intermediates/library-package-name/res/

But I guess to getting started we could say: Use the @Field annotations for library projects because you have to specify them explicit because auto detecting ViewHolder classes by scanning xml layouts does not work for library projects.

For the normal app project we could use the auto ViewHolder detection mode by scanning xml files of the app projects res folder passed as Annotation Processor option like suggested by you:

apt {
    arguments {
        androidManifestFile variant.processResources.resDir
    }
}

Do you see a better solution?

fcolasuonno commented 10 years ago

I though about that, apart from scanning recursively from the intermediates dir looking for res folders I can't think of anything else. If as layout they use android.R.layout.simple_list_item_2, for example, it won't be in the intermediates at all, because it's part of the android framework and it's located in the android sdk dir..

I think the most common case will be with the layout xml in the project, anyway.

A better name for the apt argument would probably be androidResourceDir, I forgot to change it in the previous comment, so in gradle there will be

apt {
    arguments {
        androidResourceDir variant.processResources.resDir
    }
}

and in the processor:

final String androidResourceDir = processingEnv.getOptions().get("androidResourceDir");
File layoutsDir = new File(androidResourceDir);

for (File file : layoutsDir.listFiles()) {
     processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,file.getAbsolutePath());
}
sockeqwe commented 8 years ago

Somewhere in the android gradle plugin documentation site is described how to get the path for the resource folder, but at the end you have to pass this path to your annotation processor (annotation processor can have options).

andbrain notifications@github.com schrieb am Do., 24. Dez. 2015 um 17:07 Uhr:

how are you friends i need working sample for access to resource folder from the annotation processor his codes not work for me hope helps me so soon :D

— Reply to this email directly or view it on GitHub https://github.com/sockeqwe/AnnotatedAdapter/issues/4#issuecomment-167131473 .

sockeqwe commented 8 years ago

what do you mean with "to my annotation processor?". This feature is not implemented yet in AnnotatedAdapter. Are you going to implement your own annotation processor?

andbrain notifications@github.com schrieb am Do., 24. Dez. 2015 um 17:12 Uhr:

THANK Hannes Dorfmann happy to see your replying please just tell how pass this path to your annotation processor :D

— Reply to this email directly or view it on GitHub https://github.com/sockeqwe/AnnotatedAdapter/issues/4#issuecomment-167131934 .

sockeqwe commented 8 years ago

You have to override getSupportedOptions() in your annotation processor. Like this: https://github.com/sockeqwe/fragmentargs/blob/master/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/ArgProcessor.java#L107

Then with android apt plugin you can do something like this:

apt {
  arguments {
    resPath "path/to/res/folder"
  }
}