Skip to content

Commit

Permalink
Merge pull request #1 from 2BAB/dev_v2
Browse files Browse the repository at this point in the history
v2.3.0: Support adaptive-icon
  • Loading branch information
2BAB authored Sep 30, 2018
2 parents 3ca0c7f + 1e0a945 commit 06e777a
Show file tree
Hide file tree
Showing 18 changed files with 448 additions and 182 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Only place important and recommended versions here.

## 2.3.0

- Supported adaptive-icon
- Supported android:roundIcon correctly (instead of hardcode)
- Bumped Gradle version to 4.10.2 (latest stable)
- Bugs fixed
- Code Review

## 2.2.0

- Fixed the wrong usage of buildType and flavor, now we use dimension for the combination name
- Fixed deploy script
- Bumped Gradle version to 4.6, Android Gradle Plugin to 3.2.0

## 2.1.1

- Bugs fixed
- Integrated Travis

13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
ScatchPaper can add a overlay on your icon, and put some given information on it.

- Supported regular & round Icons
- Supported adaptive-icon
- Supported AAPT2

> If you have more than one staging App for QA or other colleagues, when they found some issues you may don't know how to match the App to your commit, because all of them share the same versions like "2.1.0-SNAPSHOT".
ScatchPaper supports generating build information into your artifact (which can read from /assets/scratch-paper.json) and also `/intermedias/scratch-paper/assets` directory including:
Expand All @@ -29,8 +33,8 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'me.2bab:scratch-paper:2.2.0'
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'me.2bab:scratch-paper:2.3.0'
}
}
```
Expand All @@ -54,11 +58,11 @@ apply plugin: 'me.2bab.scratchpaper'

``` gradle
scratchPaper {
textSize = 12
textSize = 11
textColor = "#FFFFFFFF"
verticalLinePadding = 4
backgroundColor = "#99000000"
extraInfo = "This is a sample!"
extraInfo = new Date().format("MM-dd,HH:mm")
enableGenerateIconOverlay = true
enableGenerateBuildInfo = true
Expand All @@ -74,6 +78,7 @@ ScratchPaper only tests in Latest TWO Minor versions of Android Gradle Plugin.

AGP Version|Compatible Status
-----------|-----------------
3.2.x (Aapt2) | Support
3.1.x (Aapt2) | Support
3.0.x (Aapt2) | Support
2.3.x (Aapt2) | Never Tested
Expand Down
13 changes: 9 additions & 4 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
ScatchPaper 可以在你的 App icon 上加一个蒙层用以区分出各个 BuildType 的 App,并且承载了版本信息等附加文字。

- 支持 常规 和 圆形 的图标
- 支持 adaptive-icon
- 支持 AAPT2

> 如果你同时打了多个测试包给测试或者产品(例如基于多个复合分支),当他们给你反馈的问题时候你和他们可能都很难分别出每个 App 对应的具体的分支或者 commit 节点。
ScatchPaper 支持生成编译信息并打包到你的 Apk 中(从 assets 中读取),以及输出一份拷贝到 `/intermedias/scratch-paper/assets` 文件夹,包括:
Expand All @@ -29,8 +33,8 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'me.2bab:scratch-paper:2.2.0'
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'me.2bab:scratch-paper:2.3.0'
}
}
```
Expand All @@ -54,11 +58,11 @@ apply plugin: 'me.2bab.scratchpaper'

``` gradle
scratchPaper {
textSize = 12
textSize = 11
textColor = "#FFFFFFFF"
verticalLinePadding = 4
backgroundColor = "#99000000"
extraInfo = "This is a sample!"
extraInfo = new Date().format("MM-dd,HH:mm")
enableGenerateIconOverlay = true
enableGenerateBuildInfo = true
Expand All @@ -74,6 +78,7 @@ ScratchPaper 只会支持最新两个 Minor 版本的 Android Gradle Plugin:

AGP Version|Compatible Status
-----------|-----------------
3.2.x (Aapt2) | Support
3.1.x (Aapt2) | Support
3.0.x (Aapt2) | Support
2.3.x (Aapt2) | Never Tested
Expand Down
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'com.android.tools.build:gradle:3.2.0'
implementation 'org.jfree:jfreesvg:3.3'
}

compileJava {
options.compilerArgs += ["-proc:none"]
}

ext.travisBuild = System.getenv("TRAVIS") == "true"
Expand All @@ -52,6 +57,6 @@ if (project.extensions.findByName("buildScan") != null) {

// publish
group 'me.2bab'
version '2.2.0'
version '2.3.0'
apply from: 'bintray.gradle'
apply from: 'mavenlocal.gradle'
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
4 changes: 2 additions & 2 deletions sample/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ dependencies {
}

scratchPaper {
textSize = 12
textSize = 11
textColor = "#FFFFFFFF"
verticalLinePadding = 4
backgroundColor = "#99000000"
extraInfo = "This is a sample!"
extraInfo = new Date().format("MM-dd,HH:mm")
enableGenerateIconOverlay = true
enableGenerateBuildInfo = true
enableXmlIconRemove = false
Expand Down
2 changes: 1 addition & 1 deletion sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'me.2bab:scratch-paper:2.2.0'
classpath 'me.2bab:scratch-paper:2.3.0-SNAPSHOT'
}
}

Expand Down
9 changes: 9 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
rootProject.name = 'scratch-paper'

// As part of making the publishing plugins stable, the 'deferred configurable' behavior
// of the 'publishing {}' block has been deprecated.
// In Gradle 5.0 the 'enableFeaturePreview('STABLE_PUBLISHING')' flag will be removed
// and the new behavior will become the default.
// Please add 'enableFeaturePreview('STABLE_PUBLISHING')' to your settings file
// and do a test run by publishing to a local repository.
// If all artifacts are published as expected, there is nothing else to do.
enableFeaturePreview('STABLE_PUBLISHING')
11 changes: 2 additions & 9 deletions src/main/kotlin/me/xx2bab/scratchpaper/BuildInfoGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@ class BuildInfoGenerator(private val params: GeneratorParams) {
writeText(root.toJSONString())
}

// params.project.tasks.getByName("merge${params.variantCapedName}Assets").doLast(
// "generate${params.variantCapedName}BuildInfoByScratchPaper") { assetsTask ->
// val mergedAssetsDir = (assetsTask as MergeSourceSetFolders).outputDir
// val targetBIFile = File(mergedAssetsDir, buildInfoFileName)
// buildInfoFile.copyTo(targetBIFile)
// }

}
}

Expand All @@ -53,8 +46,8 @@ class BuildInfoGenerator(private val params: GeneratorParams) {

private fun generateGitInfo(): Pair<String, JSONObject> {
val git = JSONObject()
git["branch"] = CommandUtils.runCommand("git rev-parse --abbrev-ref HEAD")?.trim()
git["latestCommit"] = CommandUtils.runCommand("git rev-parse HEAD")?.trim()
git["branch"] = CommandUtils.runCommand("git rev-parse --abbrev-ref HEAD").let { it?.trim() ?: "" }
git["latestCommit"] = CommandUtils.runCommand("git rev-parse HEAD").let { it?.trim() ?: "" }
return Pair("git", git)
}

Expand Down
122 changes: 39 additions & 83 deletions src/main/kotlin/me/xx2bab/scratchpaper/IconOverlayGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,24 @@ package me.xx2bab.scratchpaper

import com.android.build.gradle.tasks.MergeManifests
import com.android.build.gradle.tasks.MergeResources
import com.android.tools.r8.com.google.common.collect.Lists
import me.xx2bab.scratchpaper.iconprocessor.BaseIconProcessor
import me.xx2bab.scratchpaper.utils.Aapt2Utils
import me.xx2bab.scratchpaper.utils.CacheUtils
import me.xx2bab.scratchpaper.utils.Logger
import org.gradle.api.Project
import java.awt.Color
import java.awt.Font
import java.awt.GraphicsEnvironment
import java.awt.RenderingHints.KEY_TEXT_ANTIALIASING
import java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import javax.xml.parsers.DocumentBuilderFactory


class IconOverlayGenerator(private val params: GeneratorParams) {

// default icon name of Android is ic_launcher
private val defaultIconName = "ic_launcher"

// to make sure the fontSize can be a regular number (like 14, 16, 18 that develops usually use)
// I test 14 on all dpi generating, and found 96 (which is the xhdpi icon size) can fits it well
// so we just make it as a standard size and compute the ratio for others to scale
private val prettyImageSizeFits14FontSize = 96.0
private val tagApplication = "application"
private val attrIcon = "android:icon"
private val attrRoundIcon = "android:roundIcon"

fun process() {
setAwtEnv()

params.variant.outputs.forEach { output ->
val processManifestTask: MergeManifests = output.processManifest as MergeManifests

Expand All @@ -39,19 +29,23 @@ class IconOverlayGenerator(private val params: GeneratorParams) {
"AndroidManifest.xml")
val resDirs = params.variant.sourceSets[0].resDirectories
val version = "@" + params.variant.mergedFlavor.versionName
val iconName = getIconName(mergedManifestFile)
findIcons(resDirs, iconName).forEach { icon ->
val processedIcon = addTextToIcon(params.project, params.dimension,
val iconNames = getIconName(mergedManifestFile)
findIcons(resDirs, iconNames).forEach { icon ->
val icons = addTextToIcon(params.project, params.dimension,
icon, params.config, params.dimension, version, params.config.extraInfo)
processedIcons.add(processedIcon)
if (icons != null) {
for (file in icons) {
processedIcons.add(file)
}
}
}

val mergeResTaskName = "merge${params.dimension}Resources"
val mergeResTask = params.project.tasks.getByName(mergeResTaskName) as MergeResources
val mergedResDir = mergeResTask.outputDir
Aapt2Utils.compileResDir(params.project, mergedResDir, processedIcons)
if (params.config.enableXmlIconRemove) {
removeXmlIconFiles(iconName, mergedResDir)
removeXmlIconFiles(iconNames, mergedResDir)
}
}
}
Expand Down Expand Up @@ -87,31 +81,36 @@ class IconOverlayGenerator(private val params: GeneratorParams) {
* Icon name to search for in the app drawable folders
* If no icon can be found in the manifest, IconOverlayGenerator#defaultIconName will be used
*/
private fun getIconName(manifestFile: File): String {
private fun getIconName(manifestFile: File): Array<String> {
if (manifestFile.isDirectory || !manifestFile.exists()) {
return ""
return arrayOf()
}
val manifestXml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(manifestFile)
val fileName = manifestXml.getElementsByTagName("application").item(0)
.attributes.getNamedItem("android:icon")?.nodeValue
return fileName?.split("/")?.get(1) ?: defaultIconName
var regularIconName = manifestXml.getElementsByTagName(tagApplication).item(0)
.attributes.getNamedItem(attrIcon)?.nodeValue
var roundIconName = manifestXml.getElementsByTagName(tagApplication).item(0)
.attributes.getNamedItem(attrRoundIcon)?.nodeValue
regularIconName = regularIconName?.split("/")?.get(1) ?: defaultIconName
roundIconName = roundIconName?.split("/")?.get(1) ?: defaultIconName + "_round"
return arrayOf(regularIconName, roundIconName)
}

/**
* Finds all icon files matching the icon specified in the given manifest.
*/
private fun findIcons(where: Collection<File>, iconName: String): List<File> {
val result: MutableList<File> = Lists.newArrayList()
private fun findIcons(where: Collection<File>, iconNames: Array<String>): Collection<File> {
val result: MutableSet<File> = hashSetOf()
where.forEach {
it.walk()
.filter { dir ->
dir.name.contains("mipmap") || dir.name.contains("drawable")
}
.forEach { file ->
file.walk().forEach { image ->
if (isIconFile(iconName, image)
&& image.extension != "xml") {
result.add(image)
iconNames.forEach { iconName ->
if (isIconFile(iconName, image)) {
result.add(image)
}
}
}
}
Expand All @@ -133,77 +132,34 @@ class IconOverlayGenerator(private val params: GeneratorParams) {
dimension: String,
image: File,
config: ScratchPaperExtension = ScratchPaperExtension.DEFAULT_CONFIG,
vararg lines: String): File {
val bufferedImage: BufferedImage = ImageIO.read(image)
val backgroundOverlayColor: Color = config.getBackgroundColor()
val textColor: Color = config.getTextColor()

val imgWidth: Int = bufferedImage.width
val imgHeight: Int = bufferedImage.height
val ratio = imgWidth / prettyImageSizeFits14FontSize

val fontSize: Int = (config.textSize * ratio).toInt()
val linePadding: Int = (config.verticalLinePadding * ratio).toInt()
val lineCount: Int = lines.size
val totalLineHeight: Int = (fontSize * lineCount) + ((linePadding + 1) * lineCount)

GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(bufferedImage).apply {
this.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON)

// Draw background overlay
this.color = backgroundOverlayColor
this.fillRect(0, imgHeight - totalLineHeight, imgWidth, totalLineHeight)

// Draw each line of text
this.font = Font(Font.SANS_SERIF, Font.PLAIN, fontSize)
this.color = textColor
for ((i, line) in lines.reversed().withIndex()) {
val strWidth = this.fontMetrics.stringWidth(line)

var x = 0
if (imgWidth >= strWidth) {
x = ((imgWidth - strWidth) / 2)
}

val y = imgHeight - (fontSize * i) - ((i + 1) * linePadding)

this.drawString(line, x, y)
}
}
val destDir = File(CacheUtils.getCacheDir(project, dimension), image.parentFile.name)
if (!destDir.exists() && !destDir.mkdirs()) {
Logger.e("Can not create cache directory for ScratchPaper.")
}
val destImage = File(destDir, image.name)
ImageIO.write(bufferedImage, "png", destImage)
return destImage
vararg lines: String): Array<File>? {
return BaseIconProcessor.getProcessor(project, dimension, image, config, lines)?.process()
}

/**
* Experimental:
* For now I didn't find an elegant approach to add a cover for xml icon,
* so the ScratchPaper provide a temporary function to remove them.
*
* @param iconName the icon defined in the AndroidManifest.xml
* @param iconNames the icons defined in the AndroidManifest.xml (icon & roundIcons)
* @param mergedResDir it's a directory like /build/intermediates/res/merged/debug
*/
private fun removeXmlIconFiles(iconName: String, mergedResDir: File) {
private fun removeXmlIconFiles(iconNames: Array<String>, mergedResDir: File) {
if (mergedResDir.isFile) {
return
}
mergedResDir.walk().forEach { file ->
if (file.isFile
&& (file.name.contains("$iconName.xml.flat")
|| file.name.contains("${iconName}_round.xml.flat"))) {
file.delete()
iconNames.forEach { iconName ->
if (file.isFile && file.name.contains("$iconName.xml.flat")) {
file.delete()
}
}

}
}

private fun isIconFile(namePrefix: String, file: File): Boolean {
return file.isFile
&& (file.nameWithoutExtension == namePrefix
|| file.nameWithoutExtension == "${namePrefix}_round")
return file.isFile && file.nameWithoutExtension == namePrefix
}


Expand Down
Loading

0 comments on commit 06e777a

Please sign in to comment.