Skip to content

Commit

Permalink
Merge pull request #6 from LikeTheSalad/release/2.0.0
Browse files Browse the repository at this point in the history
Release/2.0.0
  • Loading branch information
LikeTheSalad authored Feb 4, 2023
2 parents a206b93 + d8ddbe4 commit b1aa122
Show file tree
Hide file tree
Showing 80 changed files with 2,111 additions and 953 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Change Log
==========

Version 2.0.0 *(04-02-2023)*
---

* Using a combination of annotation processor + the new AGP Instrumentation API
526 changes: 314 additions & 212 deletions README.md

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions aaper-api/build.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
apply plugin: 'java-library'
apply plugin: 'kotlin'
apply from: '../javaUpload.gradle'

plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testImplementation 'junit:junit:4.12'
testImplementation "com.google.truth:truth:1.0.1"
testImplementation "io.mockk:mockk:1.10.0"
testImplementation "com.likethesalad.tools.testing:unit-testing:$testingUtilities_version"
}

compileKotlin {
Expand All @@ -20,4 +17,8 @@ compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import com.likethesalad.android.aaper.api.base.RequestStrategyProvider
* handling the permissions request. A default strategy will be used if not provided.
*/

@Retention(AnnotationRetention.RUNTIME)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION)
annotation class EnsurePermissions(
val permissions: Array<String>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,22 @@ object PermissionManager {
* request process.
*
* @param host - The class that contains the original method, e.g. Activity or Fragment.
* @param permissions - The array of permissions that the original method needs.
* @param originalMethod - The method annotated with [EnsurePermissions].
* @param permissions - The array of permissions that the original method needs.
* @param strategyName - The strategy name that will take care of the process for the
* originalMethod.
*/
@JvmStatic
fun processPermissionRequest(
host: Any,
permissions: Array<String>,
originalMethod: Runnable,
strategyName: String
permissions: Array<String>,
strategyName: String?
) {
val strategy = strategyProvider.getStrategy(host, strategyName)
val strategy = strategyProvider.getStrategy(
host,
strategyName ?: RequestStrategyProvider.DEFAULT_STRATEGY
)
val missingPermissions = getMissingPermissions(
host,
strategy.internalGetPermissionStatusProvider(host),
Expand Down Expand Up @@ -208,4 +211,8 @@ object PermissionManager {
private fun cleanUp() {
currentRequest = null
}

fun resetForTest() {
strategyProviderSource = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.likethesalad.android.aaper.internal.compiler

@Retention(AnnotationRetention.BINARY)
annotation class AaperMethodDef(val name: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.likethesalad.android.aaper.internal.compiler

interface AaperRunnable : Runnable
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import java.lang.reflect.Field
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import java.lang.reflect.Field

/**
* Created by César Muñoz on 10/08/20.
Expand Down Expand Up @@ -85,7 +85,7 @@ class PermissionManagerTest : BaseMockable() {
strategy.internalOnBeforeLaunchingRequest(host, any(), any())
}.returns(false)

PermissionManager.processPermissionRequest(host, permissions, originalMethod, strategyName)
PermissionManager.processPermissionRequest(host, originalMethod, permissions, strategyName)

verify {
requestLauncher.internalLaunchPermissionsRequest(
Expand All @@ -109,7 +109,7 @@ class PermissionManagerTest : BaseMockable() {
)
}.returns(true)

PermissionManager.processPermissionRequest(host, permissions, originalMethod, strategyName)
PermissionManager.processPermissionRequest(host, originalMethod, permissions, strategyName)

val capturedData = dataCaptor.captured
Truth.assertThat(capturedData.missingPermissions).isEqualTo(missingPermissions.toList())
Expand Down Expand Up @@ -138,7 +138,7 @@ class PermissionManagerTest : BaseMockable() {
val permissions = arrayOf("one", "two")
setUpPermissions(permissions, emptyArray())

PermissionManager.processPermissionRequest(host, permissions, originalMethod, strategyName)
PermissionManager.processPermissionRequest(host, originalMethod, permissions, strategyName)

verify {
originalMethod.run()
Expand All @@ -160,7 +160,7 @@ class PermissionManagerTest : BaseMockable() {
)
}.returns(false)

PermissionManager.processPermissionRequest(host, permissions, originalMethod, strategyName)
PermissionManager.processPermissionRequest(host, originalMethod, permissions, strategyName)

verify {
strategy.internalOnBeforeLaunchingRequest(host, any(), any())
Expand All @@ -179,8 +179,8 @@ class PermissionManagerTest : BaseMockable() {

PermissionManager.processPermissionRequest(
host,
secondPermissions,
secondMethod,
secondPermissions,
strategyName
)

Expand All @@ -202,7 +202,7 @@ class PermissionManagerTest : BaseMockable() {
)
}.returns(false)

PermissionManager.processPermissionRequest(host, permissions, originalMethod, strategyName)
PermissionManager.processPermissionRequest(host, originalMethod, permissions, strategyName)

verify {
strategy.internalOnBeforeLaunchingRequest(host, any(), any())
Expand All @@ -220,8 +220,8 @@ class PermissionManagerTest : BaseMockable() {

PermissionManager.processPermissionRequest(
host,
secondPermissions,
secondMethod,
secondPermissions,
strategyName
)

Expand Down
29 changes: 29 additions & 0 deletions aaper-compiler/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
id 'java-library'
id 'kotlin-kapt'
id 'org.jetbrains.kotlin.jvm'
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':aaper-api')
implementation 'com.squareup:javapoet:1.13.0'
compileOnly "com.google.auto.service:auto-service-annotations:$autoService_version"
kapt "com.google.auto.service:auto-service:$autoService_version"
testImplementation "com.google.testing.compile:compile-testing:0.21.0"
}

compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package com.likethesalad.android.aaper.compiler

import com.google.auto.service.AutoService
import com.likethesalad.android.aaper.internal.compiler.AaperMethodDef
import com.likethesalad.android.aaper.internal.compiler.AaperRunnable
import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterSpec
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import com.squareup.javapoet.TypeVariableName
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedAnnotationTypes
import javax.annotation.processing.SupportedSourceVersion
import javax.lang.model.SourceVersion
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.Name
import javax.lang.model.element.TypeElement
import javax.lang.model.element.TypeParameterElement
import javax.lang.model.element.VariableElement
import javax.lang.model.type.TypeKind
import javax.tools.Diagnostic

@AutoService(Processor::class)
@Suppress("UNCHECKED_CAST")
@SupportedAnnotationTypes("com.likethesalad.android.aaper.api.EnsurePermissions")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
class AaperProcessor : AbstractProcessor() {

override fun process(
annotations: MutableSet<out TypeElement>,
roundEnv: RoundEnvironment
): Boolean {

annotations.forEach { typeElement ->
val annotatedMethods: Set<ExecutableElement> =
roundEnv.getElementsAnnotatedWith(typeElement) as Set<ExecutableElement>

annotatedMethods.forEach { method ->
if (TypeKind.VOID != method.returnType.kind) {
processingEnv.messager.printMessage(
Diagnostic.Kind.ERROR,
"EnsurePermissions annotated methods must return void", method
)
} else {
createClassForMethod(method)
}
}
}

return true
}

private fun createClassForMethod(method: ExecutableElement) {
val containerClass = method.enclosingElement as TypeElement
val containerTypeName = TypeName.get(containerClass.asType())
val methodName = method.simpleName
val parameters = method.parameters

val constructor = createConstructor(containerTypeName, parameters)
val runMethod = createRunMethod()

val packageName = getPackageName(containerClass)
val generatedSimpleName = "Aaper_${containerClass.simpleName}_$methodName"

val typeClass = createGeneratedType(
methodName,
generatedSimpleName,
containerTypeName,
parameters,
method.typeParameters,
constructor,
runMethod
)

val javaFile = JavaFile.builder(packageName, typeClass).build()
val javaWriter =
processingEnv.filer.createSourceFile("${packageName}.$generatedSimpleName", method)
val writer = javaWriter.openWriter()

javaFile.writeTo(writer)

writer.close()
}

private fun createRunMethod(): MethodSpec {
return MethodSpec.methodBuilder("run")
.addModifiers(Modifier.PUBLIC)
.build()
}

private fun createGeneratedType(
methodName: Name,
generatedSimpleName: String,
containerTypeName: TypeName,
parameters: List<VariableElement>,
typeParameters: List<TypeParameterElement>,
vararg methods: MethodSpec
): TypeSpec {
val typeClass = TypeSpec.classBuilder(generatedSimpleName)
.addSuperinterface(AaperRunnable::class.java)
.addField(containerTypeName, "instance", Modifier.PRIVATE, Modifier.FINAL)
.addMethods(methods.asList())

typeParameters.forEach { typeParam ->
typeClass.addTypeVariable(TypeVariableName.get(typeParam))
}

parameters.forEach {
typeClass.addField(
TypeName.get(it.asType()),
it.simpleName.toString(),
Modifier.PRIVATE,
Modifier.FINAL
)
}

typeClass.addAnnotation(
AnnotationSpec.builder(AaperMethodDef::class.java)
.addMember("name", "\$S", methodName.toString())
.build()
)

return typeClass.build()
}

private fun createConstructor(
containerTypeName: TypeName,
parameters: List<VariableElement>
): MethodSpec {
val builder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(containerTypeName, "instance")
.addStatement("this.\$N = \$N", "instance", "instance")

parameters.forEach {
builder.addParameter(ParameterSpec.get(it))
builder.addStatement("this.\$N = \$N", it.simpleName, it.simpleName)
}

return builder.build()
}

private fun getPackageName(containerClass: TypeElement): String {
var packageName = containerClass.qualifiedName.toString()
val lastDot = packageName.indexOfLast { it == '.' }
if (lastDot > 0) {
packageName = packageName.substring(0, lastDot)
}
return packageName
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.likethesalad.android.aaper.compiler.AaperProcessor,isolating
Loading

0 comments on commit b1aa122

Please sign in to comment.