Skip to content

Commit

Permalink
Merge branch 'main' into liveness-user-agent
Browse files Browse the repository at this point in the history
  • Loading branch information
tjleing authored Aug 25, 2023
2 parents 5967bcc + a06a6a7 commit d8655ae
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 65 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v3
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2
with:
role-to-assume: ${{ vars.AMPLIFY_UI_ANDROID_CI_TESTS_ROLE }}
aws-region: ${{ env.AWS_REGION }}
- name: Run Unit Tests
uses: aws-actions/aws-codebuild-run-build@v1
uses: aws-actions/aws-codebuild-run-build@f202c327329cbbebd13f986f74af162a8539b5fd # v1
with:
project-name: Amplify-UI-Android-Build
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ Amplify UI for Android is an open-source UI library with cloud-connected compone
| Component | Summary | Latest Version | Docs | Sample |
| --- | --- |------------------------------------------------------------------------------------------------------| --- | --- |
| [Authenticator](authenticator) | Amplify Authenticator provides a complete drop-in implementation of an authentication flow for your application using [Amplify Authentication](https://docs.amplify.aws/lib/auth/getting-started/q/platform/android/). | [1.0.0](https://github.com/aws-amplify/amplify-ui-android/releases/tag/release_authenticator_v1.0.0) | [Docs](https://ui.docs.amplify.aws/android/connected-components/authenticator) | [Sample](samples/authenticator/) |
| [Face Liveness](liveness) | Amplify FaceLivenessDetector provides a UI component for [Amazon Rekognition Face Liveness](https://aws.amazon.com/rekognition/face-liveness/) feature that helps developers verify that only real users, not bad actors using spoofs, can access your services. | [1.1.1](https://github.com/aws-amplify/amplify-ui-android/releases/tag/release_liveness_v1.1.1) | [Docs](https://ui.docs.amplify.aws/android/connected-components/liveness) | [Sample](samples/liveness/) |
| [Face Liveness](liveness) | Amplify FaceLivenessDetector provides a UI component for [Amazon Rekognition Face Liveness](https://aws.amazon.com/rekognition/face-liveness/) feature that helps developers verify that only real users, not bad actors using spoofs, can access your services. | [1.1.2](https://github.com/aws-amplify/amplify-ui-android/releases/tag/release_liveness_v1.1.2) | [Docs](https://ui.docs.amplify.aws/android/connected-components/liveness) | [Sample](samples/liveness/) |

## Supported Versions

| Component | Version | Amplify | Material3 |
| --- |---------|---------|-----------|
| Authenticator | 1.0.0 | 2.8.4+ | 1.1.0 |
| Liveness | 1.1.1 | 2.11.1+ | 1.1.0 |
| Liveness | 1.1.2 | 2.11.1+ | 1.1.0 |

## Getting Started

Expand Down
9 changes: 9 additions & 0 deletions liveness/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## [Release 1.1.2](https://github.com/aws-amplify/amplify-ui-android/releases/tag/release_liveness_v1.1.2)
## Bug Fixes
* Update Amplify min version for Liveness in https://github.com/aws-amplify/amplify-ui-android/pull/68
* Update build.gradle in https://github.com/aws-amplify/amplify-ui-android/pull/69
* fix(liveness): Added Rekognition backend for Android app and updated README in https://github.com/aws-amplify/amplify-ui-android/pull/59
* fix(liveness): Screen rotation saves/loads correctly; lifecycle not destroyed at time of use in https://github.com/aws-amplify/amplify-ui-android/pull/65

[See all changes between 1.1.1 and 1.1.2](https://github.com/aws-amplify/amplify-ui-android/compare/release_liveness_v1.1.1...release_liveness_v1.1.2)

## [Release 1.1.1](https://github.com/aws-amplify/amplify-ui-android/releases/tag/release_liveness_v1.1.1)

### Features
Expand Down
2 changes: 1 addition & 1 deletion liveness/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ POM_ARTIFACT_ID=liveness
POM_NAME=Amplify UI Framework for Android - Liveness
POM_DESCRIPTION=Amplify UI Framework for Android - Liveness Plugin
POM_PACKAGING=aar
VERSION_NAME=1.1.1
VERSION_NAME=1.1.2
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.amplifyframework.auth.AWSCredentials
import com.amplifyframework.auth.AWSCredentialsProvider
Expand Down Expand Up @@ -138,20 +139,22 @@ internal class LivenessCoordinator(
init {
MainScope().launch {
getCameraProvider(context).apply {
unbindAll()
if (this.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA)) {
bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_FRONT_CAMERA,
preview,
analysis
)
} else {
val faceLivenessException = FaceLivenessDetectionException(
"A front facing camera is required but no front facing camera detected.",
"Enable a front facing camera."
)
processSessionError(faceLivenessException, true)
if (lifecycleOwner.lifecycle.currentState != Lifecycle.State.DESTROYED) {
unbindAll()
if (this.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA)) {
bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_FRONT_CAMERA,
preview,
analysis
)
} else {
val faceLivenessException = FaceLivenessDetectionException(
"A front facing camera is required but no front facing camera detected.",
"Enable a front facing camera."
)
processSessionError(faceLivenessException, true)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,33 +109,35 @@ fun FaceLivenessDetector(
}

// Locks portrait orientation for duration of challenge and resets on complete
LockPortraitOrientation()

Surface(color = MaterialTheme.colorScheme.background) {
if (showReadyView) {
GetReadyView {
showReadyView = false
}
} else {
AlwaysOnMaxBrightnessScreen()
ChallengeView(
key = key,
sessionId = sessionId,
region,
credentialsProvider = credentialsProvider,
onChallengeComplete = {
scope.launch {
isFinished = true
currentOnComplete.call()
}
},
onChallengeFailed = {
scope.launch {
isFinished = true
currentOnError.accept(it)
}
LockPortraitOrientation { resetOrientation ->
Surface(color = MaterialTheme.colorScheme.background) {
if (showReadyView) {
GetReadyView {
showReadyView = false
}
)
} else {
AlwaysOnMaxBrightnessScreen()
ChallengeView(
key = key,
sessionId = sessionId,
region,
credentialsProvider = credentialsProvider,
onChallengeComplete = {
scope.launch {
isFinished = true
resetOrientation()
currentOnComplete.call()
}
},
onChallengeFailed = {
scope.launch {
isFinished = true
resetOrientation()
currentOnError.accept(it)
}
}
)
}
}
}
}
Expand Down Expand Up @@ -191,7 +193,6 @@ internal fun ChallengeView(
)
}
) {

val videoViewportSize = livenessState.videoViewportSize

if (videoViewportSize != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,28 @@ package com.amplifyframework.ui.liveness.ui

import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import com.amplifyframework.ui.liveness.util.findActivity

@SuppressLint("SourceLockedOrientationActivity")
@Composable
internal fun LockPortraitOrientation() {
internal fun LockPortraitOrientation(content: @Composable (resetOrientation: () -> Unit) -> Unit) {
val context = LocalContext.current
DisposableEffect(Unit) {
val activity = context.findActivity() ?: return@DisposableEffect onDispose {}
val originalOrientation = activity.requestedOrientation
val activity = context.findActivity() ?: return content {}
val originalOrientation by rememberSaveable { mutableStateOf(activity.requestedOrientation) }
SideEffect {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
onDispose {
}

// wait until screen is rotated correctly
if (activity.resources.configuration.orientation == ORIENTATION_PORTRAIT) {
content {
activity.requestedOrientation = originalOrientation
}
}
Expand Down
23 changes: 23 additions & 0 deletions samples/backend-lambda-functions/createSession/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
RekognitionClient,
CreateFaceLivenessSessionCommand,
} from '@aws-sdk/client-rekognition';

/**
* @type {import('@types/aws-lambda').APIGatewayProxyHandler}
*/

export const handler = async (event, req) => {
const client = new RekognitionClient({ region: 'us-east-1' });
const command = new CreateFaceLivenessSessionCommand({});
const response = await client.send(command);

return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
},
body: JSON.stringify({ sessionId: response.SessionId }),
};
};
13 changes: 13 additions & 0 deletions samples/backend-lambda-functions/createSession/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "create",
"version": "2.0.0",
"main": "index.js",
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@aws-sdk/client-rekognition": "latest"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.92"
}
}
35 changes: 35 additions & 0 deletions samples/backend-lambda-functions/getResults/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
RekognitionClient,
GetFaceLivenessSessionResultsCommand,
} from '@aws-sdk/client-rekognition';

/**
* @type {import('@types/aws-lambda').APIGatewayProxyHandler}
*/

export const handler = async (event, req) => {
console.log({ req });
console.log({ event });
const client = new RekognitionClient({ region: 'us-east-1' });
const command = new GetFaceLivenessSessionResultsCommand({
SessionId: event.pathParameters.sessionId,
});
const response = await client.send(command);

const isLive = response.Confidence > 90;

return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
},
body: JSON.stringify({
isLive,
confidenceScore: response.Confidence,
auditImageBytes: Buffer.from(
new Uint8Array(Object.values(response.ReferenceImage.Bytes))
).toString('base64'),
}),
};
};
13 changes: 13 additions & 0 deletions samples/backend-lambda-functions/getResults/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "getresults",
"version": "2.0.0",
"main": "index.js",
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@aws-sdk/client-rekognition": "latest"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.92"
}
}
Loading

0 comments on commit d8655ae

Please sign in to comment.