Skip to content

Commit

Permalink
Merge pull request #1 from icerockdev/develop
Browse files Browse the repository at this point in the history
Release 0.1.0
  • Loading branch information
Alex009 authored Jan 14, 2021
2 parents 095c006 + e5b29b3 commit 6f8abd9
Show file tree
Hide file tree
Showing 42 changed files with 1,905 additions and 30 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/compilation-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: KMP library compilation check

on:
pull_request:
branches:
- master
- develop

jobs:
build:
runs-on: macOS-latest

steps:
- uses: actions/checkout@v1
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Check build
run: ./gradlew build publishToMavenLocal
18 changes: 18 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: KMP library publish

on:
release:
types: [published]

jobs:
build:
runs-on: macOS-latest

steps:
- uses: actions/checkout@v1
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Publish
run: ./gradlew publishAllPublicationsToBintrayRepository -DBINTRAY_USER=${{ secrets.BINTRAY_USER }} -DBINTRAY_KEY=${{ secrets.BINTRAY_KEY }}
39 changes: 9 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
![moko-parcelize](img/logo.png)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Download](https://api.bintray.com/packages/icerockdev/moko/moko-parcelize/images/download.svg) ](https://bintray.com/icerockdev/moko/moko-parcelize/_latestVersion) ![kotlin-version](https://img.shields.io/badge/kotlin-1.4.20-orange)
![moko-biometry](img/logo.png)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Download](https://api.bintray.com/packages/icerockdev/moko/moko-biometry/images/download.svg) ](https://bintray.com/icerockdev/moko/moko-biometry/_latestVersion) ![kotlin-version](https://img.shields.io/badge/kotlin-1.4.21-orange)

# Mobile Kotlin parcelize
This is a Kotlin Multiplatform library that supports Parcelize in common code.
# Mobile Kotlin biometry
This is a Kotlin Multiplatform library that provides authentication by FaceId and TouchId(Fingerprint)

## Table of Contents
- [Features](#features)
Expand All @@ -16,24 +16,16 @@ This is a Kotlin Multiplatform library that supports Parcelize in common code.
- [License](#license)

## Features
- **Parcelize** in common code (specially for Android target).
...

## Requirements
- Gradle version 6.0+
- Android API 16+
- iOS version 9.0+

## Versions
- kotlin 1.3.50
- kotlin 1.4.21
- 0.1.0
- kotlin 1.3.60
- 0.2.0
- kotlin 1.3.70
- 0.3.0
- kotlin 1.4.0
- 0.4.0
- kotlin 1.4.20
- 0.5.0

## Installation
root build.gradle
Expand All @@ -48,31 +40,18 @@ allprojects {
project build.gradle
```groovy
dependencies {
commonMainApi("dev.icerock.moko:parcelize:0.5.0")
commonMainApi("dev.icerock.moko:biometry:0.1.0")
}
```

Enable [kotlin android extensions](https://kotlinlang.org/docs/tutorials/android-plugin.html):
```groovy
apply plugin: 'kotlin-android-extensions'
```

## Usage
### Parcelize
Mark common code classes with the annotation `@Parcelize` like in the Android code for automatically generated `Parcelable` implementation.
```kotlin
@Parcelize
data class User(
val firstName: String,
val lastName: String
) : Parcelable
```
...

## Samples
Please see more examples in the [sample directory](sample).

## Set Up Locally
- The [parcelize directory](parcelize) contains the `parcelize` library;
- The [biometry directory](biometry) contains the `biometry` library;
- The [sample directory](sample) contains sample apps for Android and iOS; plus the mpp-library connected to the apps.

## Contributing
Expand Down
34 changes: 34 additions & 0 deletions biometry/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
plugin(Deps.Plugins.androidLibrary)
plugin(Deps.Plugins.kotlinMultiPlatform)
plugin(Deps.Plugins.androidExtensions)
plugin(Deps.Plugins.mobileMultiPlatform)
plugin(Deps.Plugins.mavenPublish)
}

group = "dev.icerock.moko"
version = Deps.mokoBiometryVersion

dependencies {
androidLibrary(Deps.Libs.Android.appCompat)
androidLibrary(Deps.Libs.Android.biometric)

mppLibrary(Deps.Libs.MultiPlatform.mokoResources)
}


publishing {
repositories.maven("https://api.bintray.com/maven/icerockdev/moko/moko-biometry/;publish=1") {
name = "bintray"

credentials {
username = System.getProperty("BINTRAY_USER")
password = System.getProperty("BINTRAY_KEY")
}
}
}

2 changes: 2 additions & 0 deletions biometry/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="dev.icerock.moko.biometry" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.biometry

import android.content.pm.PackageManager
import android.os.Build
import androidx.biometric.BiometricConstants
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import dev.icerock.moko.resources.desc.StringDesc
import dev.icerock.moko.resources.desc.desc
import java.util.concurrent.Executor
import kotlin.coroutines.suspendCoroutine

actual class BiometryAuthenticator actual constructor() {

var fragmentManager: FragmentManager? = null
private var _packageManager: PackageManager? = null

fun bind(lifecycle: Lifecycle, fragmentManager: FragmentManager) {
this.fragmentManager = fragmentManager

val observer = object : LifecycleObserver {

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroyed(source: LifecycleOwner) {
this@BiometryAuthenticator.fragmentManager = null
source.lifecycle.removeObserver(this)
}
}
lifecycle.addObserver(observer)
}

fun setPackageManager(packageManager: PackageManager) {
this._packageManager = packageManager
}

actual suspend fun checkBiometryAuthentication(
requestReason: StringDesc,
failureButtonText: StringDesc): Boolean {

val fragmentManager =
fragmentManager
?: throw IllegalStateException("can't check biometry without active window")

val currentFragment: Fragment? = fragmentManager.findFragmentByTag(BIOMETRY_RESOLVER_FRAGMENT_TAG)
val resolverFragment: ResolverFragment = if (currentFragment != null) {
currentFragment as ResolverFragment
} else {
ResolverFragment().apply {
fragmentManager
.beginTransaction()
.add(this, BIOMETRY_RESOLVER_FRAGMENT_TAG)
.commitNow()
}
}

return suspendCoroutine<Boolean> { continuation ->
var resumed = false
resolverFragment.showBiometricPrompt(
requestTitle = "Biometry".desc(),
requestReason = requestReason,
failureButtonText = failureButtonText,
credentialAllowed = true
) {
if (!resumed) {
continuation.resumeWith(it)
resumed = true
}
}
}
}

/**
* Performs a fingerprint scan availability check
*
* @return true if it is possible to use a fingerprint scanner, false - if it is not available
*/
actual fun isTouchIdEnabled(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return false
}
return _packageManager?.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
?: throw IllegalStateException("can't check touch id enabled without packageManager")
}

/**
* Performs the availability check of the FaceID scan
*
* @return true if it is possible to use the face scanner, false - if it is not available
*/
actual fun isFaceIdEnabled(): Boolean {
return false
}

class ResolverFragment : Fragment() {

private lateinit var executor: Executor
private lateinit var biometricPrompt: BiometricPrompt
private lateinit var promptInfo: BiometricPrompt.PromptInfo

init {
retainInstance = true
}

/**
* Prepare and show BiometricPrompt system dialog
*
* @param requestTitle biometric prompt title
* @param requestReason biometric prompt reason
* @param failureButtonText
* @param credentialAllowed Allows user to authenticate using their lock screen PIN, pattern, or password.
*/
fun showBiometricPrompt(
requestTitle: StringDesc,
requestReason: StringDesc,
failureButtonText: StringDesc,
credentialAllowed: Boolean,
callback: (Result<Boolean>) -> Unit
) {
val context = requireContext()

executor = ContextCompat.getMainExecutor(context)

biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int,
errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (errorCode == BiometricConstants.ERROR_NEGATIVE_BUTTON) {
callback.invoke(Result.failure(Exception(errorCode.toString())))
} else {
callback.invoke(Result.failure(Exception(errString.toString())))
}
}

override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
callback.invoke(Result.success(true))
}

override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
callback.invoke(Result.success(false))
}
}
)

promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(requestTitle.toString(context))
.setSubtitle(requestReason.toString(context))
.apply {
if (!credentialAllowed) {
this.setNegativeButtonText(failureButtonText.toString(context))
}
}
.setDeviceCredentialAllowed(credentialAllowed)
.build()

biometricPrompt.authenticate(promptInfo)
}
}

companion object {
private const val BIOMETRY_RESOLVER_FRAGMENT_TAG = "BiometryControllerResolver"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.biometry

import dev.icerock.moko.resources.desc.StringDesc

expect class BiometryAuthenticator() {

/**
* Performs user authentication using biometrics-fingerprint/face scan-returns the result of the scan
*
* @param requestReason - Text describing the reason for confirmation via biometrics
* @param failureButtonText - Text of the button to go to the backup verification method in case of unsuccessful biometrics recognition
*
* @throws Exception if authentication failed
*
* @return true for successful confirmation of biometrics, false for unsuccessful confirmation
*/

suspend fun checkBiometryAuthentication(
requestReason: StringDesc,
failureButtonText: StringDesc
): Boolean

/**
* Performs a fingerprint scan availability check
*
* @return true if it is possible to use a fingerprint scanner, false - if it is not available
*/

fun isTouchIdEnabled(): Boolean

/**
* Performs the availability check of the FaceID scan
*
* @return true if it is possible to use the face scanner, false - if it is not available
*/

fun isFaceIdEnabled(): Boolean
}
Loading

0 comments on commit 6f8abd9

Please sign in to comment.