Add automatic checks for badging

Change-Id: Ic8fca86d7aa84675fa91fb1aa99abcc6e19d663e
pull/1001/head
Alex Vanyo 1 year ago
parent 15f8861da1
commit 360b33eb82

@ -125,6 +125,9 @@ jobs:
name: lint-reports name: lint-reports
path: '**/build/reports/lint-results-*.html' path: '**/build/reports/lint-results-*.html'
- name: Check badging
run: ./gradlew :app:checkProdReleaseBadging
androidTest: androidTest:
needs: build needs: build
runs-on: macOS-latest # enables hardware acceleration in the virtual machine runs-on: macOS-latest # enables hardware acceleration in the virtual machine

@ -0,0 +1,121 @@
package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14'
sdkVersion:'21'
targetSdkVersion:'34'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.POST_NOTIFICATIONS'
uses-permission: name='android.permission.WAKE_LOCK'
uses-permission: name='com.google.android.c2dm.permission.RECEIVE'
uses-permission: name='com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE'
uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED'
uses-permission: name='android.permission.FOREGROUND_SERVICE'
uses-permission: name='com.google.samples.apps.nowinandroid.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION'
application-label:'Now in Android'
application-label-af:'Now in Android'
application-label-am:'Now in Android'
application-label-ar:'Now in Android'
application-label-as:'Now in Android'
application-label-az:'Now in Android'
application-label-be:'Now in Android'
application-label-bg:'Now in Android'
application-label-bn:'Now in Android'
application-label-bs:'Now in Android'
application-label-ca:'Now in Android'
application-label-cs:'Now in Android'
application-label-da:'Now in Android'
application-label-de:'Now in Android'
application-label-el:'Now in Android'
application-label-en-AU:'Now in Android'
application-label-en-CA:'Now in Android'
application-label-en-GB:'Now in Android'
application-label-en-IN:'Now in Android'
application-label-en-XC:'Now in Android'
application-label-es:'Now in Android'
application-label-es-US:'Now in Android'
application-label-et:'Now in Android'
application-label-eu:'Now in Android'
application-label-fa:'Now in Android'
application-label-fi:'Now in Android'
application-label-fr:'Now in Android'
application-label-fr-CA:'Now in Android'
application-label-gl:'Now in Android'
application-label-gu:'Now in Android'
application-label-hi:'Now in Android'
application-label-hr:'Now in Android'
application-label-hu:'Now in Android'
application-label-hy:'Now in Android'
application-label-in:'Now in Android'
application-label-is:'Now in Android'
application-label-it:'Now in Android'
application-label-iw:'Now in Android'
application-label-ja:'Now in Android'
application-label-ka:'Now in Android'
application-label-kk:'Now in Android'
application-label-km:'Now in Android'
application-label-kn:'Now in Android'
application-label-ko:'Now in Android'
application-label-ky:'Now in Android'
application-label-lo:'Now in Android'
application-label-lt:'Now in Android'
application-label-lv:'Now in Android'
application-label-mk:'Now in Android'
application-label-ml:'Now in Android'
application-label-mn:'Now in Android'
application-label-mr:'Now in Android'
application-label-ms:'Now in Android'
application-label-my:'Now in Android'
application-label-nb:'Now in Android'
application-label-ne:'Now in Android'
application-label-nl:'Now in Android'
application-label-or:'Now in Android'
application-label-pa:'Now in Android'
application-label-pl:'Now in Android'
application-label-pt:'Now in Android'
application-label-pt-BR:'Now in Android'
application-label-pt-PT:'Now in Android'
application-label-ro:'Now in Android'
application-label-ru:'Now in Android'
application-label-si:'Now in Android'
application-label-sk:'Now in Android'
application-label-sl:'Now in Android'
application-label-sq:'Now in Android'
application-label-sr:'Now in Android'
application-label-sr-Latn:'Now in Android'
application-label-sv:'Now in Android'
application-label-sw:'Now in Android'
application-label-ta:'Now in Android'
application-label-te:'Now in Android'
application-label-th:'Now in Android'
application-label-tl:'Now in Android'
application-label-tr:'Now in Android'
application-label-uk:'Now in Android'
application-label-ur:'Now in Android'
application-label-uz:'Now in Android'
application-label-vi:'Now in Android'
application-label-zh-CN:'Now in Android'
application-label-zh-HK:'Now in Android'
application-label-zh-TW:'Now in Android'
application-label-zu:'Now in Android'
application-icon-120:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-160:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-240:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-320:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-480:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-640:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-65534:'res/mipmap-anydpi-v26/ic_launcher.xml'
application: label='Now in Android' icon='res/mipmap-anydpi-v26/ic_launcher.xml'
launchable-activity: name='com.google.samples.apps.nowinandroid.MainActivity' label='' icon=''
uses-library-not-required:'androidx.window.extensions'
uses-library-not-required:'androidx.window.sidecar'
uses-library-not-required:'android.ext.adservices'
feature-group: label=''
uses-feature: name='android.hardware.faketouch'
uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps'
main
other-activities
other-receivers
other-services
supports-screens: 'small' 'normal' 'large' 'xlarge'
supports-any-density: 'true'
locales: '--_--' 'af' 'am' 'ar' 'as' 'az' 'be' 'bg' 'bn' 'bs' 'ca' 'cs' 'da' 'de' 'el' 'en-AU' 'en-CA' 'en-GB' 'en-IN' 'en-XC' 'es' 'es-US' 'et' 'eu' 'fa' 'fi' 'fr' 'fr-CA' 'gl' 'gu' 'hi' 'hr' 'hu' 'hy' 'in' 'is' 'it' 'iw' 'ja' 'ka' 'kk' 'km' 'kn' 'ko' 'ky' 'lo' 'lt' 'lv' 'mk' 'ml' 'mn' 'mr' 'ms' 'my' 'nb' 'ne' 'nl' 'or' 'pa' 'pl' 'pt' 'pt-BR' 'pt-PT' 'ro' 'ru' 'si' 'sk' 'sl' 'sq' 'sr' 'sr-Latn' 'sv' 'sw' 'ta' 'te' 'th' 'tl' 'tr' 'uk' 'ur' 'uz' 'vi' 'zh-CN' 'zh-HK' 'zh-TW' 'zu'
densities: '120' '160' '240' '320' '480' '640' '65534'

@ -17,11 +17,14 @@
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension
import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureGradleManagedDevices
import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.gradle.BaseExtension
import com.google.samples.apps.nowinandroid.configureBadgingTasks
import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.configurePrintApksTask
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType
class AndroidApplicationConventionPlugin : Plugin<Project> { class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
@ -39,6 +42,7 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
} }
extensions.configure<ApplicationAndroidComponentsExtension> { extensions.configure<ApplicationAndroidComponentsExtension> {
configurePrintApksTask(this) configurePrintApksTask(this)
configureBadgingTasks(extensions.getByType<BaseExtension>(), this)
} }
} }
} }

@ -0,0 +1,143 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.gradle.BaseExtension
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.register
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.process.ExecOperations
import java.io.File
import java.nio.file.Files
import javax.inject.Inject
abstract class GenerateBadgingTask : DefaultTask() {
@get:OutputFile
abstract val badging: RegularFileProperty
@get:InputFile
abstract val apk: RegularFileProperty
@get:InputFile
abstract val aapt2Executable: RegularFileProperty
@get:Inject
abstract val execOperations: ExecOperations
@TaskAction
fun taskAction() {
execOperations.exec {
commandLine(
aapt2Executable.get().asFile.absolutePath,
"dump",
"badging",
apk.get().asFile.absolutePath
)
standardOutput = badging.asFile.get().outputStream()
}
}
}
abstract class CheckBadgingTask : DefaultTask() {
// In order for the task to be up-to-date when the inputs have not changed,
// the task must declare an output, even if it's not used. Tasks with no
// output are always run regardless of whether the inputs changed
@get:OutputDirectory
abstract val output: DirectoryProperty
@get:InputFile
abstract val goldenBadging: RegularFileProperty
@get:InputFile
abstract val generatedBadging: RegularFileProperty
override fun getGroup(): String = LifecycleBasePlugin.VERIFICATION_GROUP
@TaskAction
fun taskAction() {
if (
Files.mismatch(
goldenBadging.get().asFile.toPath(),
generatedBadging.get().asFile.toPath()
) != -1L
) {
throw GradleException(
"Generated badging is different from golden badging! " +
"If this change is intended, run ./gradlew updateBadging"
)
}
}
}
fun Project.configureBadgingTasks(
baseExtension: BaseExtension,
componentsExtension: ApplicationAndroidComponentsExtension
) {
// Registers a callback to be called, when a new variant is configured
componentsExtension.onVariants { variant ->
// Registers a new task to verify the app bundle.
val generateBadging = tasks.register<GenerateBadgingTask>("generate${variant.name}Badging") {
apk.set(
variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE)
)
aapt2Executable.set(
File(
baseExtension.sdkDirectory,
"build-tools/${baseExtension.buildToolsVersion}/aapt2"
)
)
badging.set(
project.layout.buildDirectory.file(
"outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt"
)
)
}
tasks.register<Copy>("update${variant.name}Badging") {
from(generateBadging.get().badging)
into(project.layout.projectDirectory)
}
val checkBadgingTaskName = "check${variant.name}Badging"
tasks.register<CheckBadgingTask>(checkBadgingTaskName) {
goldenBadging.set(
project.layout.projectDirectory.file("${variant.name}-badging.txt"))
generatedBadging.set(
generateBadging.get().badging
)
output.set(
project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName")
)
}
}
}
Loading…
Cancel
Save