diff --git a/src/main/scala/AndroidBase.scala b/src/main/scala/AndroidBase.scala index 679b56e..c65b7e8 100644 --- a/src/main/scala/AndroidBase.scala +++ b/src/main/scala/AndroidBase.scala @@ -207,6 +207,10 @@ object AndroidBase { useProguard := true, proguardOptimizations := Seq.empty, + preserveServiceRegistry := false, + serviceRegistryInclude := Seq(".*"), + serviceRegistryExclude := Seq.empty, + jarPath <<= (platformPath, jarName) (_ / _), libraryJarPath <<= (jarPath (_ get)), diff --git a/src/main/scala/AndroidInstall.scala b/src/main/scala/AndroidInstall.scala index b923667..691cca8 100644 --- a/src/main/scala/AndroidInstall.scala +++ b/src/main/scala/AndroidInstall.scala @@ -162,13 +162,101 @@ object AndroidInstall { } } - private def packageTask(debug: Boolean):Project.Initialize[Task[File]] = (packageConfig, streams) map { (c, s) => + private def packageTask(debug: Boolean):Project.Initialize[Task[File]] = (packageConfig, fullClasspath, preserveServiceRegistry, serviceRegistryInclude, serviceRegistryExclude, streams) map { (c, cp, p, in, ex, s) => val builder = new ApkBuilder(c, debug) builder.build.fold(sys.error(_), s.log.info(_)) s.log.debug(builder.outputStream.toString) + + if (p) { + val services = extractServiceRegistry(cp.files, in, ex) + insertServiceRegistry(c.packageApkPath, services) + } + c.packageApkPath } + private def extractServiceRegistry( + cp: Seq[JFile], in: Seq[String], ex: Seq[String] + ): Map[String, List[String]] = { + import java.util.jar.JarFile + import scala.io.Source + import scala.collection.JavaConversions._ + + var ret = Map[String, List[String]]() + cp.filter(_.toString.matches(".*\\.jar")).foreach {f => { + val jarfile = new JarFile(f) + try { + for (entry <- jarfile.entries()) { + val name = entry.getName() + if (name.matches("META-INF/services/..*")) { + val is = jarfile.getInputStream(entry) + try { + val lns = Source.fromInputStream(is).getLines(). + filter(cls => in.exists(r => cls.matches(r))). + filter(cls => ex.forall(r => !cls.matches(r))) + ret += name -> (ret.getOrElse(name, List()) ++ lns) + } + finally { + is.close() + } + } + } + } + finally { + jarfile.close() + } + }} + + ret + } + + private def insertServiceRegistry(apk: File, + services: Map[String, List[String]]) = { + import java.util.zip.ZipOutputStream + import java.util.zip.ZipInputStream + import java.util.zip.ZipEntry + import java.io.FileOutputStream + import java.io.FileInputStream + import java.io.File + + val tmp = new File(apk.getAbsolutePath + ".tmp") + tmp.delete() + + if (!apk.renameTo(tmp)) { + throw new RuntimeException("could not rename file " + apk) + } + val buf = Array.ofDim[Byte](1024) + + val in = new ZipInputStream(new FileInputStream(tmp)) + val out = new ZipOutputStream(new FileOutputStream(apk)) + + try { + var entry = in.getNextEntry() + + while (entry != null) { + out.putNextEntry(new ZipEntry(entry.getName)) + var len = in.read(buf) + while (len > 0) { + out.write(buf, 0, len) + len = in.read(buf) + } + entry = in.getNextEntry() + } + + for ((name, value) <- services) { + if (value.size > 0) { + out.putNextEntry(new ZipEntry(name)) + out.write(value.mkString("\n").getBytes) + } + } + } + finally { + tmp.delete() + in.close() + out.close() + } + } + lazy val installerTasks = Seq ( installEmulator <<= installTask(emulator = true) dependsOn packageDebug, installDevice <<= installTask(emulator = false) dependsOn packageDebug diff --git a/src/main/scala/AndroidKeys.scala b/src/main/scala/AndroidKeys.scala index f8de3b5..62d5d9f 100644 --- a/src/main/scala/AndroidKeys.scala +++ b/src/main/scala/AndroidKeys.scala @@ -72,6 +72,10 @@ object AndroidKeys { val packageApkLibPath = TaskKey[File]("package-apklib-path") val useProguard = SettingKey[Boolean]("use-proguard") + val preserveServiceRegistry = SettingKey[Boolean]("preserve-service-registry") + val serviceRegistryInclude = SettingKey[Seq[String]]("service-registry-include") + val serviceRegistryExclude = SettingKey[Seq[String]]("service-registry-exclude") + /** Install Settings */ val packageConfig = TaskKey[ApkConfig]("package-config", "Generates a Apk Config") @@ -81,7 +85,7 @@ object AndroidKeys { val typedResource = TaskKey[File]("typed-resource", """Typed resource file to be generated, also includes interfaces to access these resources.""") - val layoutResources = TaskKey[Seq[File]]("layout-resources", + val layoutResources = TaskKey[Seq[File]]("layout-resources", """All files that are in res/layout. They will be accessable through TR.layouts._""")