155b52578fdaca7097ea4b28b3daebac84094dab4Christian Williamspackage org.robolectric.internal;
2b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
3851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.io.File;
4851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.net.URL;
52a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisieimport java.nio.file.Files;
62a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisieimport java.nio.file.Path;
72a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisieimport java.nio.file.Paths;
8b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayarimport org.robolectric.annotation.Config;
9b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayarimport org.robolectric.res.FileFsFile;
10b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayarimport org.robolectric.util.Logger;
11b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayarimport org.robolectric.util.ReflectionHelpers;
12b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
1355b52578fdaca7097ea4b28b3daebac84094dab4Christian Williamspublic class GradleManifestFactory implements ManifestFactory {
14b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  @Override
15eb01f4f02fcd34067b8ff5e53bfeb9575b874e1fChristian Williams  public ManifestIdentifier identify(Config config) {
16b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    if (config.constants() == Void.class) {
17b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      Logger.error("Field 'constants' not specified in @Config annotation");
1855b52578fdaca7097ea4b28b3daebac84094dab4Christian Williams      Logger.error("This is required when using Robolectric with Gradle!");
19b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      throw new RuntimeException("No 'constants' field in @Config annotation!");
20b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    }
21b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
22b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    final String buildOutputDir = getBuildOutputDir(config);
23b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    final String type = getType(config);
24b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    final String flavor = getFlavor(config);
25d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish    final String abiSplit = getAbiSplit(config);
2655b52578fdaca7097ea4b28b3daebac84094dab4Christian Williams    final String packageName = config.packageName().isEmpty()
2755b52578fdaca7097ea4b28b3daebac84094dab4Christian Williams        ? config.constants().getPackage().getName()
2855b52578fdaca7097ea4b28b3daebac84094dab4Christian Williams        : config.packageName();
29b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
30b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    final FileFsFile res;
31b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    final FileFsFile assets;
32b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    final FileFsFile manifest;
33b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
34b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    if (FileFsFile.from(buildOutputDir, "data-binding-layout-out").exists()) {
35b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      // Android gradle plugin 1.5.0+ puts the merged layouts in data-binding-layout-out.
36b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      // https://github.com/robolectric/robolectric/issues/2143
37b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      res = FileFsFile.from(buildOutputDir, "data-binding-layout-out", flavor, type);
38b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } else if (FileFsFile.from(buildOutputDir, "res", "merged").exists()) {
39b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      // res/merged added in Android Gradle plugin 1.3-beta1
40b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      res = FileFsFile.from(buildOutputDir, "res", "merged", flavor, type);
41b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } else if (FileFsFile.from(buildOutputDir, "res").exists()) {
42b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      res = FileFsFile.from(buildOutputDir, "res", flavor, type);
43b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } else {
44b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      res = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "res");
45b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    }
46b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
474c8cedc6e3cecbee3b30dce84d7746a9b527812cScott Kennedy    if (!Config.DEFAULT_ASSET_FOLDER.equals(config.assetDir())
484c8cedc6e3cecbee3b30dce84d7746a9b527812cScott Kennedy            && FileFsFile.from(buildOutputDir, config.assetDir()).exists()) {
494c8cedc6e3cecbee3b30dce84d7746a9b527812cScott Kennedy      assets = FileFsFile.from(buildOutputDir, config.assetDir());
504c8cedc6e3cecbee3b30dce84d7746a9b527812cScott Kennedy    } else if (FileFsFile.from(buildOutputDir, "assets").exists()) {
51b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      assets = FileFsFile.from(buildOutputDir, "assets", flavor, type);
52b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } else {
53b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      assets = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "assets");
54b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    }
55b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
56b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams    String manifestName = config.manifest();
57b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams    URL manifestUrl = getClass().getClassLoader().getResource(manifestName);
58b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams    if (manifestUrl != null && manifestUrl.getProtocol().equals("file")) {
59b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams      manifest = FileFsFile.from(manifestUrl.getPath());
60b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams    } else if (FileFsFile.from(buildOutputDir, "manifests", "full").exists()) {
61b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams      manifest = FileFsFile.from(buildOutputDir, "manifests", "full", flavor, abiSplit, type, manifestName);
6224973cd7fa61401b3af1069f01d516e40080221cHilal Alsibai    } else if (FileFsFile.from(buildOutputDir, "manifests", "aapt").exists()) {
6324973cd7fa61401b3af1069f01d516e40080221cHilal Alsibai      // Android gradle plugin 2.2.0+ can put library manifest files inside of "aapt" instead of "full"
64b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams      manifest = FileFsFile.from(buildOutputDir, "manifests", "aapt", flavor, abiSplit, type, manifestName);
65b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } else {
66b4d0e3b1d6313b7f37852f42a2b8b54cc57af17eChristian Williams      manifest = FileFsFile.from(buildOutputDir, "bundles", flavor, abiSplit, type, manifestName);
67b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    }
68b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
69eb01f4f02fcd34067b8ff5e53bfeb9575b874e1fChristian Williams    return new ManifestIdentifier(manifest, res, assets, packageName, null);
70eb01f4f02fcd34067b8ff5e53bfeb9575b874e1fChristian Williams  }
71eb01f4f02fcd34067b8ff5e53bfeb9575b874e1fChristian Williams
72b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  private static String getBuildOutputDir(Config config) {
732a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie    Path buildDir = Paths.get(config.buildDir(), "intermediates");
742a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie    if (!Files.exists(buildDir)) {
752a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      // By default build dir is a relative path. However, the build dir lookup may fail if the
762a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      // working directory of the test configuration in Android Studio is not set to the module
772a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      // root directory (e.g it is set to the entire project root directory). Attempt to locate it
782a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      // relative to the constants class, which is generated in the build output directory.
792a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      String moduleRoot = config.constants().getResource("").toString().replace("file:", "");
802a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      int idx = moduleRoot.lastIndexOf(File.separator + "intermediates");
812a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      if (idx > 0) {
822a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie        buildDir = Paths.get(moduleRoot.substring(0, idx), "intermediates");
832a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      } else {
842a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie        Logger.error("Failed to locate build dir");
852a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie      }
862a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie    }
872a324a6cbb9db3b2ccfafc4435760d8e1a04d746Michael Hoisie    return buildDir.toString();
88b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  }
89b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
90b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  private static String getType(Config config) {
91b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    try {
92b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      return ReflectionHelpers.getStaticField(config.constants(), "BUILD_TYPE");
93b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } catch (Throwable e) {
94b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      return null;
95b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    }
96b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  }
97b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
98b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  private static String getFlavor(Config config) {
99b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    try {
100b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      return ReflectionHelpers.getStaticField(config.constants(), "FLAVOR");
101b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    } catch (Throwable e) {
102b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar      return null;
103b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar    }
104b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar  }
105b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar
106d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish  private static String getAbiSplit(Config config) {
107d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish    try {
108d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish      return config.abiSplit();
109d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish    } catch (Throwable e) {
110d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish      return null;
111d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish    }
112d7812f81cb863ffc228a6beec03eb365115aa1f3Jonathan Gerrish  }
113b4c08d70a2f549cf344c81bb20c824d3cc31c298Vijay Nayar}
114