Slide 1

Slide 1 text

PermissionsDispatcher ✖ Kotlin

Slide 2

Slide 2 text

• PermissionsDispatcher • Generate Runtime Permissions code • 100% reflection-free • Special permissions support • Xiaomi support • committer: @shiraji, @aurae

Slide 3

Slide 3 text

• 3.0.0(beta) • fully Kotlin support • Why? • to support inline modifier • to make API Kotlin-ish • Now it’s official

Slide 4

Slide 4 text

repositories { jcenter() maven { url ‘http://oss.jfrog.org/artifactory/oss-snapshot-local/' } } dependencies { compile(“com.github.hotchemi:permissionsdispatcher:3.0.0-SNAPSHOT”) { exclude module: "support-v13" } kapt "com.github.hotchemi:permissionsdispatcher-processor:3.0.0-SNAPSHOT" }

Slide 5

Slide 5 text

@RuntimePermissions
 class MainActivity extends AppCompatActivity Java

Slide 6

Slide 6 text

@NeedsPermission(Manifest.permission.CAMERA)
 void showCamera(); MainActivityPermissionsDispatcher.showCameraWithCheck(this); Java

Slide 7

Slide 7 text

@RuntimePermissions(kotlin = true)
 class MainActivity : AppCompatActivity() Kotlin

Slide 8

Slide 8 text

@NeedsPermission(Manifest.permission.CAMERA)
 inline fun showCamera() showCameraWithCheck() Kotlin

Slide 9

Slide 9 text

private val REQUEST_SHOWCAMERA: Int = 0
 private val PERMISSION_SHOWCAMERA: Array = arrayOf("android.permission.CAMERA")
 
 fun MainActivity.showCameraWithCheck() {
 if (PermissionUtils.hasSelfPermissions(this, PERMISSION_SHOWCAMERA)) {
 showCamera()
 } else {
 if (PermissionUtils.shouldShowRequestPermissionRationale(this, PERMISSION_SHOWCAMERA)) {
 showRationaleForCamera(ShowCameraPermissionRequest(this))
 } else {
 ActivityCompat.requestPermissions(this, PERMISSION_SHOWCAMERA, REQUEST_SHOWCAMERA)
 }
 }
 }
 
 fun MainActivity.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray): Unit {
 when (requestCode) {
 REQUEST_SHOWCAMERA ->
 if (PermissionUtils.verifyPermissions(*grantResults)) {
 showCamera()
 } else {
 if (!PermissionUtils.shouldShowRequestPermissionRationale(this, PERMISSION_SHOWCAMERA)) {
 onCameraNeverAskAgain()
 } else {
 onCameraDenied()
 }
 }
 }
 }
 
 private class ShowCameraPermissionRequest(target: MainActivity) : PermissionRequest {
 private val weakTarget: WeakReference = WeakReference(target)
 override fun proceed() {
 val target = weakTarget.get() ?: return
 ActivityCompat.requestPermissions(target, PERMISSION_SHOWCAMERA, REQUEST_SHOWCAMERA)
 }
 override fun cancel() {
 val target = weakTarget.get() ?: return
 target.onCameraDenied()
 }
 }

Slide 10

Slide 10 text

• Under the hood • generate .kt file at compile time • KotlinPoet • kapt3 • Testing

Slide 11

Slide 11 text

• KotlinPoet • Kotlin version of JavaPoet • DSL for generating source files • latest ver: 0.3.0 • early-access release • KDoc is not updated • writeTo(filer: Filer) is not supported

Slide 12

Slide 12 text

class Greeter(val name: String) { fun greet() { println("Hello, $name") } } fun main(vararg args: String) { Greeter(args[0]).greet() }

Slide 13

Slide 13 text

val greeterClass = ClassName("", "Greeter") val kotlinFile = KotlinFile.builder("", "HelloWorld") .addType(TypeSpec.classBuilder("Greeter") .primaryConstructor(FunSpec.constructorBuilder() .addParameter("name", String::class) .build()) .addProperty(PropertySpec.builder("name", String::class) .initializer("name") .build()) .addFun(FunSpec.builder("greet") .addStatement("println(%S)", "Hello, \$name") .build()) .build()) .addFun(FunSpec.builder("main") .addParameter("args", String::class, VARARG) .addStatement("%T(args[0]).greet()", greeterClass) .build()) .build() kotlinFile.writeTo(System.out)

Slide 14

Slide 14 text

if (isKotlin) { val kaptGeneratedDirPath = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]?.replace("kaptKotlin", "kap processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Can't find the target directory for generated Kot return false } val kaptGeneratedDir = File(kaptGeneratedDirPath) if (!kaptGeneratedDir.parentFile.exists()) { kaptGeneratedDir.parentFile.mkdirs() } val processorUnits = listOf(ActivityKtProcessorUnit(), SupportFragmentKtProcessorUnit(), NativeFragmentKtProces val processorUnit = findAndValidateKtProcessorUnit(processorUnits, it) val kotlinFile = processorUnit.createKotlinFile(rpe, requestCodeProvider) kotlinFile.writeTo(kaptGeneratedDir) } else { val processorUnits = listOf(ActivityProcessorUnit(), SupportFragmentProcessorUnit(), NativeFragmentProcessorUni val processorUnit = findAndValidateProcessorUnit(processorUnits, it) val javaFile = processorUnit.createJavaFile(rpe, requestCodeProvider) javaFile.writeTo(filer) }

Slide 15

Slide 15 text

• kapt3 • supports .kt file generation • kt generated/source/kaptKotlin/$sourceSet • java generated/source/kapt/$sourceSet • apt generated/source/apt/$sourceSet • processingEnv.
 options[“kapt.kotlin.generated”]

Slide 16

Slide 16 text

• kapt3 • But… • kaptKotlin dir was not recognized correctly with Android project… • worked well only on java project • filed a bug report • youtrack.jetbrains.com/issue/KT-19097

Slide 17

Slide 17 text

• Testing • we can’t use google/compile-testing • write tests for behavior, not code itself • with PowerMockito, Robolectric • check test, test-v13 projects • read Testing Against Annotation Processing • by @shiraji san

Slide 18

Slide 18 text

• What learned • Supporting Java/Kotlin is tough work than we expected • class delegation is a way to go? • Anyway it was fun:D

Slide 19

Slide 19 text

• Misc • 3.0.0 would be officially released soon! • Hopefully end of July • Give us feedback!

Slide 20

Slide 20 text

Thank you