├── testing
├── .gitignore
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── amplifyframework
│ │ └── ui
│ │ └── testing
│ │ ├── CoroutineTestRule.kt
│ │ ├── ComposeRobot.kt
│ │ └── ComposeTest.kt
└── build.gradle.kts
├── authenticator
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── test
│ │ ├── screenshots
│ │ │ ├── SignInTest_default-state.png
│ │ │ ├── SignUpTest_default-state.png
│ │ │ ├── SignUpTest_invalid-email.png
│ │ │ ├── SignInTest_password-visible.png
│ │ │ ├── SignInTest_ready-to-submit.png
│ │ │ ├── SignUpTest_invalid-password.png
│ │ │ ├── SignUpTest_password-visible.png
│ │ │ ├── SignUpTest_ready-to-submit.png
│ │ │ ├── SignUpTest_username-exists.png
│ │ │ ├── SignInTest_username-not-found.png
│ │ │ ├── PasskeyCreatedTest_done-selected.png
│ │ │ ├── PasswordResetTest_default-state.png
│ │ │ ├── SignInConfirmMfaTest_default-state.png
│ │ │ ├── SignUpTest_passwordless-with-email.png
│ │ │ ├── SignUpTest_passwords-do-not-match.png
│ │ │ ├── PasskeyCreatedTest_with-one-passkey.png
│ │ │ ├── PasswordResetTest_username-not-found.png
│ │ │ ├── SignInConfirmMfaTest_incorrect-code.png
│ │ │ ├── SignUpTest_passwordless-with-username.png
│ │ │ ├── PasskeyCreationPromptTest_default-state.png
│ │ │ ├── PasswordResetConfirmTest_default-state.png
│ │ │ ├── SignInConfirmPasswordTest_default-state.png
│ │ │ ├── SignInConfirmTotpCodeTest_default-state.png
│ │ │ ├── SignInConfirmTotpCodeTest_invalid-code.png
│ │ │ ├── SignInSelectAuthFactorTest_no-password.png
│ │ │ ├── PasskeyCreatedTest_with-multiple-passkeys.png
│ │ │ ├── PasskeyCreationPromptTest_creating-passkey.png
│ │ │ ├── SignInConfirmPasswordTest_password-visible.png
│ │ │ ├── SignInConfirmPasswordTest_ready-to-submit.png
│ │ │ ├── PasswordResetConfirmTest_passwords-do-not-match.png
│ │ │ ├── SignInContinueWithEmailSetupTest_default-state.png
│ │ │ ├── SignInContinueWithTotpSetupTest_default-state.png
│ │ │ ├── SignInContinueWithMfaSelectionTest_default-state.png
│ │ │ ├── PasskeyCreationPromptTest_skipping-passkey-creation.png
│ │ │ ├── PasswordResetConfirmTest_incorrect-confirmation-code.png
│ │ │ ├── SignInConfirmPasswordTest_ready-to-submit-with-email.png
│ │ │ ├── SignInSelectAuthFactorTest_default-state-with-all-factors.png
│ │ │ ├── SignInConfirmPasswordTest_ready-to-submit-with-phonenumber.png
│ │ │ ├── SignInSelectAuthFactorTest_default-state-with-all-factors-with-email.png
│ │ │ └── SignInSelectAuthFactorTest_default-state-with-all-factors-with-phone-number.png
│ │ └── java
│ │ │ └── com
│ │ │ └── amplifyframework
│ │ │ └── ui
│ │ │ └── authenticator
│ │ │ ├── ui
│ │ │ ├── robots
│ │ │ │ ├── PasskeyCreatedRobot.kt
│ │ │ │ ├── PasskeyCreationPromptRobot.kt
│ │ │ │ ├── SignInConfirmMfaRobot.kt
│ │ │ │ ├── PasswordResetConfirmRobot.kt
│ │ │ │ ├── PasswordResetRobot.kt
│ │ │ │ ├── SignInConfirmTotpCodeRobot.kt
│ │ │ │ ├── SignInContinueWithEmailSetupRobot.kt
│ │ │ │ ├── SignInContinueWithTotpSetupRobot.kt
│ │ │ │ ├── SignInRobot.kt
│ │ │ │ ├── SignInContinueWithMfaSelectionRobot.kt
│ │ │ │ ├── SignInConfirmPasswordRobot.kt
│ │ │ │ ├── SignInSelectAuthFactorRobot.kt
│ │ │ │ ├── ScreenLevelRobot.kt
│ │ │ │ └── SignUpRobot.kt
│ │ │ ├── SignInContinueWithMfaSetupSelectionTest.kt
│ │ │ └── SignInConfirmMfaTest.kt
│ │ │ ├── util
│ │ │ └── ExceptionsTest.kt
│ │ │ └── testUtil
│ │ │ └── AuthenticatorUiTest.kt
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── amplifyframework
│ │ │ └── ui
│ │ │ └── authenticator
│ │ │ ├── util
│ │ │ ├── AuthenticatorUiConstants.kt
│ │ │ ├── OsBuild.kt
│ │ │ ├── ContextExtensions.kt
│ │ │ ├── AmplifyResult.kt
│ │ │ ├── Errors.kt
│ │ │ ├── Autofill.kt
│ │ │ └── PasskeyPromptCheck.kt
│ │ │ ├── enums
│ │ │ └── SignInSource.kt
│ │ │ ├── states
│ │ │ ├── MutableActionState.kt
│ │ │ ├── PasskeyCreatedStateImpl.kt
│ │ │ ├── SignInConfirmTotpCodeStateImpl.kt
│ │ │ ├── PromptToCreatePasskeyStateImpl.kt
│ │ │ ├── SignInContinueWithTotpSetupStateImpl.kt
│ │ │ ├── SignedInStateImpl.kt
│ │ │ ├── SignInConfirmPasswordStateImpl.kt
│ │ │ ├── SignInContinueWithMfaSelectionStateImpl.kt
│ │ │ ├── SignInContinueWithEmailSetupStateImpl.kt
│ │ │ ├── VerifyUserStateImpl.kt
│ │ │ ├── VerifyUserConfirmStateImpl.kt
│ │ │ ├── PasswordResetStateImpl.kt
│ │ │ ├── SignInConfirmMfaStateImpl.kt
│ │ │ ├── SignInConfirmCustomStateImpl.kt
│ │ │ ├── SignUpConfirmStateImpl.kt
│ │ │ ├── SignInConfirmNewPasswordStateImpl.kt
│ │ │ ├── SignInStateImpl.kt
│ │ │ ├── SignInContinueWithMfaSetupSelectionStateImpl.kt
│ │ │ ├── BaseStateImpl.kt
│ │ │ └── SignInSelectAuthFactorStateImpl.kt
│ │ │ ├── locals
│ │ │ ├── LocalAuthenticatorStep.kt
│ │ │ └── LocalStringResolver.kt
│ │ │ ├── data
│ │ │ ├── UserInfo.kt
│ │ │ └── PasskeyPrompt.kt
│ │ │ ├── options
│ │ │ └── TotpOptions.kt
│ │ │ ├── ui
│ │ │ ├── CommonFooter.kt
│ │ │ ├── AuthenticatorTitle.kt
│ │ │ ├── DividerWithText.kt
│ │ │ ├── AuthenticatorError.kt
│ │ │ ├── DeliveryDetails.kt
│ │ │ ├── QrCode.kt
│ │ │ ├── AuthenticatorButton.kt
│ │ │ ├── RadioGroup.kt
│ │ │ └── AuthenticatorForm.kt
│ │ │ └── AuthenticatorConfiguration.kt
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── ic_authenticator_clear.xml
│ │ │ ├── ic_authenticator_calendar.xml
│ │ │ ├── ic_authenticator_search.xml
│ │ │ ├── authenticator_success.xml
│ │ │ ├── ic_authenticator_visible.xml
│ │ │ └── ic_authenticator_invisible.xml
│ │ └── values
│ │ │ ├── messages.xml
│ │ │ └── errors.xml
│ │ └── AndroidManifest.xml
├── build.gradle.kts
└── gradle.properties
├── liveness
├── .gitignore
├── src
│ ├── test
│ │ └── resources
│ │ │ └── robolectric.properties
│ └── main
│ │ ├── assets
│ │ └── face_detection_short_range.tflite
│ │ ├── java
│ │ └── com
│ │ │ └── amplifyframework
│ │ │ └── ui
│ │ │ └── liveness
│ │ │ ├── util
│ │ │ ├── WebSocketCloseCode.kt
│ │ │ └── Extensions.kt
│ │ │ ├── state
│ │ │ └── AttemptCounter.kt
│ │ │ ├── ui
│ │ │ ├── LivenessPreviewContainer.kt
│ │ │ ├── AlwaysOnMaxBrightnessScreen.kt
│ │ │ └── LockPortraitOrientation.kt
│ │ │ ├── model
│ │ │ └── Freshness.kt
│ │ │ └── ml
│ │ │ └── FaceOval.kt
│ │ ├── cpp
│ │ ├── liveness_jni_hooks.cpp
│ │ └── CMakeLists.txt
│ │ └── AndroidManifest.xml
├── README.md
├── gradle.properties
└── build.gradle.kts
├── NOTICE
├── samples
├── liveness
│ ├── app
│ │ ├── .gitignore
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── values
│ │ │ │ │ └── themes.xml
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ ├── mipmap-anydpi-v33
│ │ │ │ │ └── ic_launcher.xml
│ │ │ │ └── drawable
│ │ │ │ │ ├── ic_baseline_error_24.xml
│ │ │ │ │ └── ic_outline_content_copy_24.xml
│ │ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── amplifyframework
│ │ │ │ │ └── ui
│ │ │ │ │ └── sample
│ │ │ │ │ └── liveness
│ │ │ │ │ ├── ui
│ │ │ │ │ └── LivenessScreen.kt
│ │ │ │ │ ├── LivenessApp.kt
│ │ │ │ │ └── LivenessSampleBackend.kt
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle.kts
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── build.gradle.kts
│ ├── .gitignore
│ ├── settings.gradle.kts
│ └── gradle.properties
├── authenticator
│ ├── app
│ │ ├── .gitignore
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── themes.xml
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ └── mipmap-anydpi-v33
│ │ │ │ │ └── ic_launcher.xml
│ │ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── amplifyframework
│ │ │ │ │ └── ui
│ │ │ │ │ └── sample
│ │ │ │ │ └── authenticator
│ │ │ │ │ ├── theme
│ │ │ │ │ └── default
│ │ │ │ │ │ ├── Color.kt
│ │ │ │ │ │ └── Type.kt
│ │ │ │ │ ├── AuthenticatorSampleApp.kt
│ │ │ │ │ └── data
│ │ │ │ │ └── ThemeDatastore.kt
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle.kts
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ ├── .gitignore
│ └── gradle.properties
└── backend-lambda-functions
│ ├── createSession
│ ├── package.json
│ └── index.js
│ └── getResults
│ ├── package.json
│ └── index.js
├── configuration
├── consumer-rules.pro
└── java.header
├── .editorconfig
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .github
├── CODEOWNERS
├── workflows
│ ├── notify_release.yml
│ ├── issue_labeled.yml
│ ├── notify_pull_request.yml
│ ├── codecov_code_coverage.yml
│ ├── issue_closed.yml
│ ├── issue_comment.yml
│ └── release_pr.yml
└── PULL_REQUEST_TEMPLATE.md
├── scripts
├── fastlane
│ ├── Appfile
│ └── Pluginfile
└── Gemfile
├── codecov.yml
├── CODE_OF_CONDUCT.md
├── settings.gradle.kts
├── gradle.properties
└── .gitignore
/testing/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/authenticator/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/liveness/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /buildNative
--------------------------------------------------------------------------------
/liveness/src/test/resources/robolectric.properties:
--------------------------------------------------------------------------------
1 | sdk=28
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
--------------------------------------------------------------------------------
/samples/liveness/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /buildNative
3 | **/awsconfiguration.json
4 | **/amplifyconfiguration**.json
--------------------------------------------------------------------------------
/configuration/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | -keepclassmembers enum * { *; }
2 |
3 | -keep class com.amplifyframework.** { *; }
4 |
--------------------------------------------------------------------------------
/samples/authenticator/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /buildNative
3 | **/awsconfiguration.json
4 | **/amplifyconfiguration**.json
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{kt,kts}]
2 | #this is to match java checkstyle
3 | max_line_length=120
4 | ktlint_code_style=android_studio
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/samples/liveness/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/samples/authenticator/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @aws-amplify/amplify-android @aws-amplify/amplify-ui
2 |
3 | # Changes to api surface require admin approval
4 | *.api @aws-amplify/amplify-android-admins
5 |
--------------------------------------------------------------------------------
/liveness/src/main/assets/face_detection_short_range.tflite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/liveness/src/main/assets/face_detection_short_range.tflite
--------------------------------------------------------------------------------
/authenticator/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | # Keep AuthExceptions names since these can be mapped to error strings reflectively
2 | -keepnames class * extends com.amplifyframework.auth.AuthException
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_invalid-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_invalid-email.png
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInTest_password-visible.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInTest_password-visible.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInTest_ready-to-submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInTest_ready-to-submit.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_invalid-password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_invalid-password.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_password-visible.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_password-visible.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_ready-to-submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_ready-to-submit.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_username-exists.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_username-exists.png
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInTest_username-not-found.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInTest_username-not-found.png
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasskeyCreatedTest_done-selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasskeyCreatedTest_done-selected.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasswordResetTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasswordResetTest_default-state.png
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/liveness/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmMfaTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmMfaTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_passwordless-with-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_passwordless-with-email.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_passwords-do-not-match.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_passwords-do-not-match.png
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasskeyCreatedTest_with-one-passkey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasskeyCreatedTest_with-one-passkey.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasswordResetTest_username-not-found.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasswordResetTest_username-not-found.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmMfaTest_incorrect-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmMfaTest_incorrect-code.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignUpTest_passwordless-with-username.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignUpTest_passwordless-with-username.png
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/samples/authenticator/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/scripts/fastlane/Appfile:
--------------------------------------------------------------------------------
1 | json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
2 | package_name("com.amplifyframework.ui") # e.g. com.krausefx.app
3 |
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasskeyCreationPromptTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasskeyCreationPromptTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasswordResetConfirmTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasswordResetConfirmTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmPasswordTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmPasswordTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_invalid-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_invalid-code.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_no-password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_no-password.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasskeyCreatedTest_with-multiple-passkeys.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasskeyCreatedTest_with-multiple-passkeys.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasskeyCreationPromptTest_creating-passkey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasskeyCreationPromptTest_creating-passkey.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmPasswordTest_password-visible.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmPasswordTest_password-visible.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmPasswordTest_ready-to-submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmPasswordTest_ready-to-submit.png
--------------------------------------------------------------------------------
/samples/authenticator/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application) apply false
3 | alias(libs.plugins.kotlin.android) apply false
4 | alias(libs.plugins.compose.compiler) apply false
5 | }
6 |
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasswordResetConfirmTest_passwords-do-not-match.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasswordResetConfirmTest_passwords-do-not-match.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInContinueWithEmailSetupTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInContinueWithEmailSetupTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInContinueWithTotpSetupTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInContinueWithTotpSetupTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInContinueWithMfaSelectionTest_default-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInContinueWithMfaSelectionTest_default-state.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasskeyCreationPromptTest_skipping-passkey-creation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasskeyCreationPromptTest_skipping-passkey-creation.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/PasswordResetConfirmTest_incorrect-confirmation-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/PasswordResetConfirmTest_incorrect-confirmation-code.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmPasswordTest_ready-to-submit-with-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmPasswordTest_ready-to-submit-with-email.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_default-state-with-all-factors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_default-state-with-all-factors.png
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInConfirmPasswordTest_ready-to-submit-with-phonenumber.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInConfirmPasswordTest_ready-to-submit-with-phonenumber.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/samples/liveness/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_default-state-with-all-factors-with-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_default-state-with-all-factors-with-email.png
--------------------------------------------------------------------------------
/samples/authenticator/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/scripts/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "fastlane"
4 | gem "addressable", ">= 2.8.0"
5 | gem "rexml", ">= 3.4.2"
6 |
7 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
8 | eval_gemfile(plugins_path) if File.exist?(plugins_path)
9 |
--------------------------------------------------------------------------------
/samples/liveness/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application) apply false
3 | alias(libs.plugins.kotlin.android) apply false
4 | alias(libs.plugins.compose.compiler) apply false
5 | alias(libs.plugins.kotlin.serialization) apply false
6 | }
7 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AuthenticatorUiConstants.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.util
2 |
3 | import androidx.compose.ui.unit.dp
4 |
5 | internal object AuthenticatorUiConstants {
6 | val spaceBetweenFields = 8.dp
7 | }
8 |
--------------------------------------------------------------------------------
/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_default-state-with-all-factors-with-phone-number.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-amplify/amplify-ui-android/HEAD/authenticator/src/test/screenshots/SignInSelectAuthFactorTest_default-state-with-all-factors-with-phone-number.png
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/util/WebSocketCloseCode.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.liveness.util
2 |
3 | internal enum class WebSocketCloseCode(val code: Int) {
4 | TIMEOUT(4001),
5 | CANCELED(4003),
6 | RUNTIME_ERROR(4005),
7 | DISPOSED(4008)
8 | }
9 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | target: auto
6 | threshold: 0.1%
7 | patch:
8 | default:
9 | target: auto
10 | threshold: 0%
11 | comment:
12 | layout: diff
13 | behavior: default
14 | require_changes: false
15 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/enums/SignInSource.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.enums
2 |
3 | internal enum class SignInSource {
4 | // Standard sign in
5 | SignIn,
6 |
7 | // Automatic sign in after completing sign up
8 | AutoSignIn
9 | }
10 |
--------------------------------------------------------------------------------
/samples/liveness/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/OsBuild.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.util
2 |
3 | import android.os.Build
4 |
5 | // Facade for android.os.Build to facilitate testing
6 | internal class OsBuild {
7 | val sdkInt: Int
8 | get() = Build.VERSION.SDK_INT
9 | }
10 |
--------------------------------------------------------------------------------
/scripts/fastlane/Pluginfile:
--------------------------------------------------------------------------------
1 | # Autogenerated by fastlane
2 | #
3 | # Ensure this file is checked in to source control!
4 |
5 | gem 'fastlane-plugin-release_actions', git: 'https://github.com/aws-amplify/amplify-ci-support', branch: 'android/fastlane-actions', glob: 'src/fastlane/release_actions/*.gemspec'
6 | gem 'fastlane-plugin-semantic_release'
7 |
--------------------------------------------------------------------------------
/samples/backend-lambda-functions/createSession/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create",
3 | "version": "2.0.0",
4 | "main": "index.js",
5 | "license": "Apache-2.0",
6 | "type": "module",
7 | "dependencies": {
8 | "@aws-sdk/client-rekognition": "latest"
9 | },
10 | "devDependencies": {
11 | "@types/aws-lambda": "^8.10.92"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/backend-lambda-functions/getResults/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "getresults",
3 | "version": "2.0.0",
4 | "main": "index.js",
5 | "license": "Apache-2.0",
6 | "type": "module",
7 | "dependencies": {
8 | "@aws-sdk/client-rekognition": "latest"
9 | },
10 | "devDependencies": {
11 | "@types/aws-lambda": "^8.10.92"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/drawable/ic_authenticator_clear.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/liveness/README.md:
--------------------------------------------------------------------------------
1 | # Face Liveness
2 |
3 | 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.
4 |
5 | More information on setting up and using the FaceLivenessDetector is in the [Amplify UI Face Liveness documentation](https://ui.docs.amplify.aws/android/connected-components/liveness).
6 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/MutableActionState.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import com.amplifyframework.ui.authenticator.AuthenticatorActionState
4 |
5 | internal interface MutableActionState : AuthenticatorActionState {
6 | override var action: T?
7 | }
8 |
9 | internal inline fun MutableActionState.withAction(action: T, func: () -> Unit) {
10 | this.action = action
11 | func()
12 | this.action = null
13 | }
14 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/ContextExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.util
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.ContextWrapper
6 |
7 | /**
8 | * Allows us to get the Activity reference from Compose LocalContext
9 | */
10 | internal tailrec fun Context.findActivity(): Activity? = when (this) {
11 | is Activity -> this
12 | is ContextWrapper -> this.baseContext.findActivity()
13 | else -> null
14 | }
15 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/locals/LocalAuthenticatorStep.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.locals
2 |
3 | import androidx.compose.runtime.compositionLocalOf
4 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
5 |
6 | /**
7 | * This composition local supplies the current AuthenticatorStep. This allows descendant composables to tailor
8 | * their content to specific steps.
9 | */
10 | internal val LocalAuthenticatorStep = compositionLocalOf { AuthenticatorStep.Loading }
11 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/drawable/ic_authenticator_calendar.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/drawable/ic_authenticator_search.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AmplifyResult.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.util
2 |
3 | import com.amplifyframework.auth.AuthException
4 |
5 | internal sealed interface AmplifyResult {
6 | data class Success(val data: T) : AmplifyResult
7 | data class Error(val error: AuthException) : AmplifyResult
8 | }
9 |
10 | internal inline fun AmplifyResult.getOrDefault(crossinline provider: () -> T) = when (this) {
11 | is AmplifyResult.Error -> provider()
12 | is AmplifyResult.Success -> this.data
13 | }
14 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/data/UserInfo.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.data
2 |
3 | import com.amplifyframework.ui.authenticator.enums.SignInSource
4 |
5 | internal data class UserInfo(
6 | val username: String,
7 | val password: String?,
8 | val signInSource: SignInSource,
9 | val selectedAuthFactor: AuthFactor? = null
10 | ) {
11 | override fun toString() = "UserInfo(" +
12 | "username=$username, " +
13 | "password=***, " +
14 | "signInSource=$signInSource, " +
15 | "selectedAuthFactor=$selectedAuthFactor)"
16 | }
17 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("build-logic")
3 | repositories {
4 | gradlePluginPortal()
5 | google()
6 | mavenCentral()
7 | }
8 | }
9 |
10 | dependencyResolutionManagement {
11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | }
17 |
18 | rootProject.name = "amplify-ui-android"
19 | include(":liveness")
20 | include(":authenticator")
21 | include(":testing")
22 |
23 | // Enable typesafe accessor generation for cross-project references
24 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
25 |
--------------------------------------------------------------------------------
/.github/workflows/notify_release.yml:
--------------------------------------------------------------------------------
1 | name: Notify Amplify UI Android Release
2 |
3 | on:
4 | release:
5 | types: [created, published]
6 |
7 | jobs:
8 | notify:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Run webhook curl command
12 | env:
13 | WEBHOOK_URL: ${{ secrets.SLACK_RELEASE_WEBHOOK_URL }}
14 | VERSION: ${{github.event.release.html_url}}
15 | REPO_URL: ${{github.event.repository.html_url}}
16 | ACTION_NAME: ${{github.event.action}}
17 | shell: bash
18 | run: echo $VERSION | xargs -I {} curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data '{"action":"'$ACTION_NAME'", "repo":"'$REPO_URL'", "version":"{}"}'
19 |
--------------------------------------------------------------------------------
/configuration/java.header:
--------------------------------------------------------------------------------
1 | ^/\*$
2 | ^ \* Copyright \d{4} Amazon\.com, Inc\. or its affiliates\. All Rights Reserved\.$
3 | ^ \*$
4 | ^ \* Licensed under the Apache License, Version 2\.0 \(the \"License\"\)\.$
5 | ^ \* You may not use this file except in compliance with the License\.$
6 | ^ \* A copy of the License is located at$
7 | ^ \*$
8 | ^ \* http://aws\.amazon\.com/apache2\.0$
9 | ^ \*$
10 | ^ \* or in the \"license\" file accompanying this file\. This file is distributed$
11 | ^ \* on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either$
12 | ^ \* express or implied\. See the License for the specific language governing$
13 | ^ \* permissions and limitations under the License\.$
14 | ^ \*/$
15 |
16 |
--------------------------------------------------------------------------------
/.github/workflows/issue_labeled.yml:
--------------------------------------------------------------------------------
1 | name: Issue Labeled
2 | on:
3 | issues:
4 | types: [labeled]
5 |
6 | jobs:
7 | remove-pending-triage-label:
8 | runs-on: ubuntu-latest
9 | if: ${{ contains(fromJSON('["question", "bug", "feature-request"]'), github.event.label.name) }}
10 | permissions:
11 | issues: write
12 | steps:
13 | - name: Remove the pending-triage label
14 | shell: bash
15 | env:
16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 | ISSUE_NUMBER: ${{ github.event.issue.number }}
18 | REPOSITORY_NAME: ${{ github.event.repository.full_name }}
19 | run: |
20 | gh issue edit $ISSUE_NUMBER --repo $REPOSITORY_NAME --remove-label "pending-triage"
--------------------------------------------------------------------------------
/samples/liveness/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("../../build-logic")
3 | repositories {
4 | google()
5 | mavenCentral()
6 | gradlePluginPortal()
7 | }
8 | }
9 |
10 | dependencyResolutionManagement {
11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | versionCatalogs {
17 | create("libs") {
18 | from(files("../../gradle/libs.versions.toml"))
19 | }
20 | }
21 | }
22 |
23 | rootProject.name = "Liveness-Sample"
24 | include(":app")
25 |
26 | // Uncomment this to use local liveness module from Amplify UI Repo
27 | //includeBuild("../../") {}
28 |
--------------------------------------------------------------------------------
/.github/workflows/notify_pull_request.yml:
--------------------------------------------------------------------------------
1 | name: Notify Pull Request
2 |
3 | on:
4 | pull_request:
5 | types: [opened, ready_for_review, reopened]
6 |
7 | jobs:
8 | notify:
9 | runs-on: ubuntu-latest
10 | if: ${{ !github.event.draft }}
11 | steps:
12 | - name: Run webhook curl command
13 | env:
14 | WEBHOOK_URL: ${{ secrets.SLACK_PR_WEBHOOK_URL }}
15 | URL: ${{ github.event.pull_request.html_url }}
16 | TITLE: ${{ github.event.pull_request.title }}
17 | USER: ${{ github.event.pull_request.user.login }}
18 | shell: bash
19 | run: curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data "{\"url\":\"$URL\", \"title\":\"$TITLE\", \"user\":\"$USER\"}"
20 |
--------------------------------------------------------------------------------
/authenticator/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/samples/authenticator/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("amplify.android.application")
3 | }
4 |
5 | android {
6 | namespace = "com.amplifyframework.ui.sample.authenticator"
7 | }
8 |
9 | dependencies {
10 | val authenticatorVersion = "1.6.0"
11 |
12 | implementation(platform(libs.androidx.compose.bom))
13 | implementation("com.amplifyframework.ui:authenticator:$authenticatorVersion")
14 |
15 | implementation(libs.bundles.compose)
16 | implementation(libs.androidx.compose.material.icons)
17 | implementation(libs.androidx.lifecycle)
18 | implementation(libs.androidx.activity.compose)
19 | implementation(libs.samples.androidx.datastore.prefs)
20 |
21 | coreLibraryDesugaring(libs.android.desugar)
22 | }
23 |
--------------------------------------------------------------------------------
/samples/authenticator/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("../../build-logic")
3 | repositories {
4 | google()
5 | mavenCentral()
6 | gradlePluginPortal()
7 | }
8 | }
9 |
10 | dependencyResolutionManagement {
11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | versionCatalogs {
17 | create("libs") {
18 | from(files("../../gradle/libs.versions.toml"))
19 | }
20 | }
21 | }
22 |
23 | rootProject.name = "Authenticator-Sample"
24 | include(":app")
25 |
26 | // Uncomment this to use local authenticator module from Amplify UI Repo
27 | includeBuild("../../") {}
28 |
--------------------------------------------------------------------------------
/samples/backend-lambda-functions/createSession/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | RekognitionClient,
3 | CreateFaceLivenessSessionCommand,
4 | } from '@aws-sdk/client-rekognition';
5 |
6 | /**
7 | * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
8 | */
9 |
10 | export const handler = async (event, req) => {
11 | const client = new RekognitionClient({ region: 'us-east-1' });
12 | const command = new CreateFaceLivenessSessionCommand({});
13 | const response = await client.send(command);
14 |
15 | return {
16 | statusCode: 200,
17 | headers: {
18 | 'Access-Control-Allow-Origin': '*',
19 | 'Access-Control-Allow-Headers': '*',
20 | },
21 | body: JSON.stringify({ sessionId: response.SessionId }),
22 | };
23 | };
24 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasskeyCreatedRobot.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui.robots
2 |
3 | import androidx.compose.ui.test.junit4.ComposeTestRule
4 | import com.amplifyframework.ui.authenticator.ui.TestTags
5 | import com.amplifyframework.ui.testing.ComposeTest
6 |
7 | fun ComposeTest.passkeyCreated(func: PasskeyCreatedRobot.() -> Unit) = PasskeyCreatedRobot(composeTestRule).func()
8 |
9 | class PasskeyCreatedRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
10 | fun hasContinueButton(expected: String) = assertExists(TestTags.ContinueButton, expected)
11 | fun hasPasskeyText(text: String) = assertExists(text)
12 |
13 | fun clickContinueButton() = clickOnTag(TestTags.ContinueButton)
14 | }
15 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/options/TotpOptions.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.options
2 |
3 | /**
4 | * Options for configuring the
5 | * [TOTP MFA](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa-totp.html) experience.
6 | */
7 | data class TotpOptions(
8 | /**
9 | * The 'issuer' is the title displayed in a user's authenticator application, preceding the account name.
10 | * In most cases this should be the name of your app. For example, if your app is called "My App", your user
11 | * will see "My App - Username" in their authenticator application.
12 | * Defaults to the name of the Android application if not supplied.
13 | */
14 | val issuer: String? = null
15 | )
16 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 | Amplify Authenticator
18 |
19 |
--------------------------------------------------------------------------------
/testing/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/liveness/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License").
5 | # You may not use this file except in compliance with the License.
6 | # A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/apache2.0
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed
11 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | # express or implied. See the License for the specific language governing
13 | # permissions and limitations under the License.
14 | #
15 |
16 | POM_ARTIFACT_ID=liveness
17 | POM_NAME=Amplify UI Framework for Android - Liveness
18 | POM_DESCRIPTION=Amplify UI Framework for Android - Liveness Plugin
19 | POM_PACKAGING=aar
20 | VERSION_NAME=1.8.2
21 |
--------------------------------------------------------------------------------
/samples/authenticator/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
17 | #amplify-do-not-edit-begin
18 | amplify/\#current-cloud-backend
19 | amplify/.config/local-*
20 | amplify/logs
21 | amplify/mock-data
22 | amplify/mock-api-resources
23 | amplify/backend/amplify-meta.json
24 | amplify/backend/.temp
25 | build/
26 | dist/
27 | node_modules/
28 | aws-exports.js
29 | awsconfiguration.json
30 | amplifyconfiguration.json
31 | amplifyconfiguration.dart
32 | amplify-build-config.json
33 | amplify-gradle-config.json
34 | amplifytools.xcconfig
35 | .secret-*
36 | **.sample
37 | #amplify-do-not-edit-end
38 |
--------------------------------------------------------------------------------
/authenticator/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("amplify.android.ui.component")
3 | }
4 |
5 | android {
6 | namespace = "com.amplifyframework.ui.authenticator"
7 | defaultConfig {
8 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
9 | consumerProguardFiles += file("consumer-rules.pro")
10 | }
11 |
12 | compileOptions {
13 | isCoreLibraryDesugaringEnabled = true
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(platform(libs.androidx.compose.bom))
19 |
20 | api(libs.amplify.auth)
21 |
22 | implementation(libs.bundles.compose)
23 | implementation(libs.androidx.lifecycle)
24 | implementation(libs.androidx.compose.viewmodel)
25 | implementation(libs.zxing)
26 | coreLibraryDesugaring(libs.android.desugar)
27 |
28 | testImplementation(projects.testing)
29 | }
30 |
--------------------------------------------------------------------------------
/authenticator/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License").
5 | # You may not use this file except in compliance with the License.
6 | # A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/apache2.0
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed
11 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | # express or implied. See the License for the specific language governing
13 | # permissions and limitations under the License.
14 | #
15 |
16 | POM_ARTIFACT_ID=authenticator
17 | POM_NAME=Amplify UI Framework for Android - Authenticator
18 | POM_DESCRIPTION=Amplify UI Framework for Android - Authenticator Plugin
19 | POM_PACKAGING=aar
20 | VERSION_NAME=1.8.0
21 |
--------------------------------------------------------------------------------
/liveness/src/main/cpp/liveness_jni_hooks.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 |
19 | extern "C" {
20 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
21 | return JNI_VERSION_1_6;
22 | }
23 | }
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.github/workflows/codecov_code_coverage.yml:
--------------------------------------------------------------------------------
1 | name: Run code coverage
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 | branches:
9 | - 'main'
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
17 |
18 | - name: Setup Java
19 | uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3
20 | with:
21 | java-version: '17'
22 | distribution: 'corretto'
23 |
24 | - name: Run test and generate kover report
25 | run: ./gradlew koverXmlReportCoverage
26 |
27 | - name: Upload Test Report
28 | uses: codecov/codecov-action@v5
29 | with:
30 | name: report
31 | files: build/reports/kover/reportCoverage.xml
32 | token: ${{ secrets.CODECOV_TOKEN }}
33 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/values/messages.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 | Password has been reset
18 | Verification code sent
19 |
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | - [ ] PR conforms to [Pull Request](https://github.com/aws-amplify/amplify-ui-android/blob/main/CONTRIBUTING.md#contributing-via-pull-requests) guidelines.
2 |
3 | *Issue #, if available:*
4 |
5 | *Description of changes:*
6 |
7 | *How did you test these changes?*
8 | (Please add a line here how the changes were tested)
9 |
10 | *Documentation update required?*
11 | - [ ] No
12 | - [ ] Yes (Please include a PR link for the documentation update)
13 |
14 | *General Checklist*
15 | - [ ] Added Unit Tests
16 | - [ ] Added Integration Tests
17 | - [ ] Security oriented best practices and standards are followed (e.g. using input sanitization, principle of least privilege, etc)
18 | - [ ] Ensure commit message has the appropriate scope (e.g `fix(liveness): message`, `fix(authenticator): message`, `fix(all): message`)
19 |
20 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
21 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/locals/LocalStringResolver.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.locals
17 |
18 | import androidx.compose.runtime.staticCompositionLocalOf
19 | import com.amplifyframework.ui.authenticator.strings.StringResolver
20 |
21 | internal val LocalStringResolver = staticCompositionLocalOf { StringResolver() }
22 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasskeyCreationPromptRobot.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui.robots
2 |
3 | import androidx.compose.ui.test.junit4.ComposeTestRule
4 | import com.amplifyframework.ui.authenticator.ui.TestTags
5 | import com.amplifyframework.ui.testing.ComposeTest
6 |
7 | fun ComposeTest.passkeyCreationPrompt(func: PasskeyCreationPromptRobot.() -> Unit) =
8 | PasskeyCreationPromptRobot(composeTestRule).func()
9 |
10 | class PasskeyCreationPromptRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
11 | fun hasCreatePasskeyButton(expected: String) = assertExists(TestTags.CreatePasskeyButton, expected)
12 | fun hasSkipPasskeyButton(expected: String) = assertExists(TestTags.SkipPasskeyButton, expected)
13 | fun clickCreatePasskeyButton() = clickOnTag(TestTags.CreatePasskeyButton)
14 | fun clickSkipPasskeyButton() = clickOnTag(TestTags.SkipPasskeyButton)
15 | fun hasPromptText(text: String) = assertExists(text)
16 | }
17 |
--------------------------------------------------------------------------------
/testing/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | plugins {
17 | id("amplify.android.library")
18 | }
19 |
20 | android {
21 | namespace = "com.amplifyframework.ui.testing"
22 | }
23 |
24 | dependencies {
25 | implementation(platform(libs.androidx.compose.bom))
26 |
27 | api(libs.bundles.test)
28 | implementation(libs.bundles.compose)
29 |
30 | implementation(libs.test.roborazzi)
31 | }
32 |
--------------------------------------------------------------------------------
/liveness/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/drawable/authenticator_success.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/liveness/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("amplify.android.application")
3 | alias(libs.plugins.kotlin.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.amplifyframework.ui.sample.liveness"
8 | defaultConfig {
9 | buildConfigField("boolean", "SHOW_DEBUG_UI", "false")
10 | }
11 | }
12 |
13 | dependencies {
14 | val livenessVersion = "1.6.0"
15 |
16 | implementation(platform(libs.androidx.compose.bom))
17 | implementation("com.amplifyframework.ui:liveness:$livenessVersion")
18 |
19 | implementation(libs.amplify.core.kotlin)
20 | implementation(libs.amplify.api)
21 | implementation(libs.amplify.auth)
22 | implementation(libs.amplify.predictions)
23 |
24 | implementation(libs.bundles.compose)
25 | implementation(libs.androidx.lifecycle)
26 | implementation(libs.androidx.activity.compose)
27 | implementation(libs.androidx.navigation.compose)
28 | implementation(libs.accompanist)
29 |
30 | coreLibraryDesugaring(libs.android.desugar)
31 | }
32 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/PasskeyCreatedStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.amplifyframework.auth.result.AuthWebAuthnCredential
7 | import com.amplifyframework.ui.authenticator.PasskeyCreatedState
8 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
9 |
10 | internal class PasskeyCreatedStateImpl(
11 | override val passkeys: List,
12 | private val onDone: suspend () -> Unit
13 | ) : PasskeyCreatedState,
14 | MutableActionState {
15 | override val step: AuthenticatorStep = AuthenticatorStep.PasskeyCreated
16 |
17 | override var action: PasskeyCreatedState.Action? by mutableStateOf(null)
18 |
19 | override suspend fun continueSignIn() = withAction(PasskeyCreatedState.Action.ContinueSignIn()) {
20 | onDone()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/java/com/amplifyframework/ui/sample/authenticator/theme/default/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.sample.authenticator.theme.default
17 |
18 | import androidx.compose.ui.graphics.Color
19 |
20 | val Purple80 = Color(0xFFD0BCFF)
21 | val PurpleGrey80 = Color(0xFFCCC2DC)
22 | val Pink80 = Color(0xFFEFB8C8)
23 |
24 | val Purple40 = Color(0xFF6650a4)
25 | val PurpleGrey40 = Color(0xFF625b71)
26 | val Pink40 = Color(0xFF7D5260)
27 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/drawable/ic_baseline_error_24.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/backend-lambda-functions/getResults/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | RekognitionClient,
3 | GetFaceLivenessSessionResultsCommand,
4 | } from '@aws-sdk/client-rekognition';
5 |
6 | /**
7 | * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
8 | */
9 |
10 | export const handler = async (event, req) => {
11 | console.log({ req });
12 | console.log({ event });
13 | const client = new RekognitionClient({ region: 'us-east-1' });
14 | const command = new GetFaceLivenessSessionResultsCommand({
15 | SessionId: event.pathParameters.sessionId,
16 | });
17 | const response = await client.send(command);
18 |
19 | const isLive = response.Confidence > 90;
20 |
21 | return {
22 | statusCode: 200,
23 | headers: {
24 | 'Access-Control-Allow-Origin': '*',
25 | 'Access-Control-Allow-Headers': '*',
26 | },
27 | body: JSON.stringify({
28 | isLive,
29 | confidenceScore: response.Confidence,
30 | auditImageBytes: Buffer.from(
31 | new Uint8Array(Object.values(response.ReferenceImage.Bytes))
32 | ).toString('base64'),
33 | }),
34 | };
35 | };
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInConfirmTotpCodeStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import com.amplifyframework.ui.authenticator.SignInConfirmTotpCodeState
4 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
5 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
6 | import com.amplifyframework.ui.authenticator.forms.FieldKey
7 |
8 | internal class SignInConfirmTotpCodeStateImpl(
9 | private val onSubmit: suspend (confirmationCode: String) -> Unit,
10 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
11 | ) : BaseStateImpl(), SignInConfirmTotpCodeState {
12 | init {
13 | form.addFields {
14 | confirmationCode()
15 | }
16 | }
17 |
18 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
19 |
20 | override suspend fun confirmSignIn() = doSubmit {
21 | val confirmationCode = form.getTrimmed(FieldKey.ConfirmationCode)!!
22 | onSubmit(confirmationCode)
23 | }
24 |
25 | override val step = AuthenticatorStep.SignInConfirmTotpCode
26 | }
27 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/util/ExceptionsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.util
17 |
18 | import io.kotest.matchers.shouldBe
19 | import org.junit.Test
20 |
21 | class ExceptionsTest {
22 |
23 | @Test
24 | fun `InvalidConfigurationException maps to the expected resource name`() {
25 | val exception = InvalidConfigurationException("test", null)
26 | exception.toResourceName() shouldBe "amplify_ui_authenticator_error_invalid_configuration"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/res/drawable/ic_outline_content_copy_24.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/CommonFooter.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.material3.Text
6 | import androidx.compose.material3.TextButton
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.platform.testTag
11 | import androidx.compose.ui.res.stringResource
12 | import com.amplifyframework.ui.authenticator.R
13 |
14 | @Composable
15 | internal fun BackToSignInFooter(
16 | onClickBackToSignIn: () -> Unit,
17 | modifier: Modifier = Modifier,
18 | label: String = stringResource(R.string.amplify_ui_authenticator_button_back_to_signin)
19 | ) {
20 | Box(
21 | modifier = modifier.fillMaxWidth(),
22 | contentAlignment = Alignment.Center
23 | ) {
24 | TextButton(
25 | modifier = Modifier.testTag(TestTags.BackToSignInButton),
26 | onClick = onClickBackToSignIn
27 | ) {
28 | Text(label)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/liveness/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2020 The Android Open Source Project
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | # use this file except in compliance with the License. You may obtain a copy of
6 | # the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | # License for the specific language governing permissions and limitations under
14 | # the License.
15 | #
16 | project (liveness-native)
17 |
18 | cmake_minimum_required(VERSION 3.4.1)
19 |
20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
21 |
22 | add_library(
23 | liveness_opengl_renderer_jni SHARED
24 | liveness_jni_hooks.cpp
25 | liveness_opengl_renderer_jni.cpp)
26 |
27 | find_library(log-lib log)
28 | find_library(android-lib android)
29 | find_library(opengl-lib GLESv2)
30 | find_library(egl-lib EGL)
31 |
32 |
33 | target_link_libraries(liveness_opengl_renderer_jni ${log-lib} ${android-lib} ${opengl-lib} ${egl-lib})
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/PromptToCreatePasskeyStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.amplifyframework.ui.authenticator.PromptToCreatePasskeyState
7 | import com.amplifyframework.ui.authenticator.PromptToCreatePasskeyState.Action
8 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
9 | import kotlinx.coroutines.sync.Mutex
10 | import kotlinx.coroutines.sync.withLock
11 |
12 | class PromptToCreatePasskeyStateImpl(private val onSubmit: suspend () -> Unit, private val onSkip: suspend () -> Unit) :
13 | PromptToCreatePasskeyState,
14 | MutableActionState {
15 | private val mutex = Mutex()
16 |
17 | override val step = AuthenticatorStep.PasskeyCreationPrompt
18 |
19 | override var action: Action? by mutableStateOf(null)
20 |
21 | override suspend fun createPasskey() = withAction(Action.CreatePasskey()) {
22 | mutex.withLock {
23 | onSubmit()
24 | }
25 | }
26 |
27 | override suspend fun skip() = withAction(Action.Skip()) { onSkip() }
28 | }
29 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithTotpSetupStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import com.amplifyframework.ui.authenticator.SignInContinueWithTotpSetupState
4 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
5 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
6 | import com.amplifyframework.ui.authenticator.forms.FieldKey
7 |
8 | internal class SignInContinueWithTotpSetupStateImpl(
9 | override val sharedSecret: String,
10 | override val setupUri: String,
11 | private val onSubmit: suspend (confirmationCode: String) -> Unit,
12 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
13 | ) : BaseStateImpl(), SignInContinueWithTotpSetupState {
14 |
15 | init {
16 | form.addFields {
17 | confirmationCode()
18 | }
19 | }
20 |
21 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
22 |
23 | override suspend fun continueSignIn() = doSubmit {
24 | val confirmationCode = form.getTrimmed(FieldKey.ConfirmationCode)!!
25 | onSubmit(confirmationCode)
26 | }
27 |
28 | override val step = AuthenticatorStep.SignInContinueWithTotpSetup
29 | }
30 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/drawable/ic_authenticator_visible.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelectionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui
17 |
18 | import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
19 | import com.amplifyframework.ui.authenticator.testUtil.mockSignInContinueWithMfaSetupSelectionState
20 | import org.junit.Test
21 |
22 | class SignInContinueWithMfaSetupSelectionTest : AuthenticatorUiTest() {
23 | @Test
24 | fun `default state`() {
25 | setContent {
26 | SignInContinueWithMfaSetupSelection(mockSignInContinueWithMfaSetupSelectionState())
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignedInStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.auth.AuthUser
19 | import com.amplifyframework.auth.result.AuthSignOutResult
20 | import com.amplifyframework.ui.authenticator.SignedInState
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
22 |
23 | internal class SignedInStateImpl(
24 | override val user: AuthUser,
25 | private val onSignOut: suspend () -> AuthSignOutResult
26 | ) : SignedInState {
27 | override val step = AuthenticatorStep.SignedIn
28 | override suspend fun signOut() = onSignOut()
29 | }
30 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorConfiguration.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator
17 |
18 | import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
19 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
20 | import com.amplifyframework.ui.authenticator.forms.SignUpFormBuilder
21 | import com.amplifyframework.ui.authenticator.options.TotpOptions
22 |
23 | internal data class AuthenticatorConfiguration(
24 | val initialStep: AuthenticatorInitialStep,
25 | val signUpForm: SignUpFormBuilder.() -> Unit,
26 | val totpOptions: TotpOptions?,
27 | val authenticationFlow: AuthenticationFlow
28 | )
29 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInConfirmMfaRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.junit4.ComposeTestRule
19 | import com.amplifyframework.ui.authenticator.forms.FieldKey
20 | import com.amplifyframework.ui.testing.ComposeTest
21 |
22 | fun ComposeTest.signInConfirmMfa(func: SignInConfirmMfaRobot.() -> Unit) = SignInConfirmMfaRobot(
23 | composeTestRule
24 | ).func()
25 |
26 | class SignInConfirmMfaRobot(composeTestRule: ComposeTestRule) : ScreenLevelRobot(composeTestRule) {
27 |
28 | fun setConfirmationCode(value: String) = setFieldContent(FieldKey.ConfirmationCode, value)
29 | }
30 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasswordResetConfirmRobot.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui.robots
2 |
3 | import androidx.compose.ui.autofill.ContentType
4 | import androidx.compose.ui.test.junit4.ComposeTestRule
5 | import com.amplifyframework.ui.authenticator.forms.FieldKey
6 | import com.amplifyframework.ui.authenticator.ui.TestTags
7 | import com.amplifyframework.ui.testing.ComposeTest
8 |
9 | fun ComposeTest.passwordResetConfirm(func: PasswordResetConfirmRobot.() -> Unit) =
10 | PasswordResetConfirmRobot(composeTestRule).func()
11 |
12 | class PasswordResetConfirmRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
13 | fun hasSubmitButton(expected: String) = assertExists(TestTags.PasswordResetButton, expected)
14 | fun hasPasswordContentType(contentType: ContentType) = hasContentType(FieldKey.Password, contentType)
15 | fun hasConfirmPasswordContentType(contentType: ContentType) = hasContentType(FieldKey.ConfirmPassword, contentType)
16 |
17 | fun setConfirmationCode(value: String) = setFieldContent(FieldKey.ConfirmationCode, value)
18 | fun setPassword(value: String) = setFieldContent(FieldKey.Password, value)
19 | fun setConfirmPassword(value: String) = setFieldContent(FieldKey.ConfirmPassword, value)
20 | fun clickBackToSignIn() = clickOnTag(TestTags.BackToSignInButton)
21 | }
22 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasswordResetRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.junit4.ComposeTestRule
19 | import com.amplifyframework.ui.authenticator.forms.FieldKey
20 | import com.amplifyframework.ui.authenticator.ui.TestTags
21 | import com.amplifyframework.ui.testing.ComposeTest
22 |
23 | fun ComposeTest.passwordReset(func: PasswordResetRobot.() -> Unit) = PasswordResetRobot(composeTestRule).func()
24 |
25 | class PasswordResetRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
26 | fun hasSubmitButton(expected: String) = assertExists(TestTags.PasswordResetButton, expected)
27 |
28 | fun setUsername(value: String) = setFieldContent(FieldKey.Username, value)
29 | }
30 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInConfirmPasswordStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import com.amplifyframework.ui.authenticator.SignInConfirmPasswordState
4 | import com.amplifyframework.ui.authenticator.auth.SignInMethod
5 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
6 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
7 | import com.amplifyframework.ui.authenticator.forms.FieldKey
8 |
9 | internal class SignInConfirmPasswordStateImpl(
10 | override val username: String,
11 | val signInMethod: SignInMethod,
12 | private val onSubmit: suspend (password: String) -> Unit,
13 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
14 | ) : BaseStateImpl(),
15 | SignInConfirmPasswordState {
16 |
17 | init {
18 | form.addFields {
19 | password()
20 | }
21 | }
22 |
23 | override val step: AuthenticatorStep = AuthenticatorStep.SignInConfirmPassword
24 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
25 |
26 | override suspend fun signIn() = doSubmit {
27 | val password = form.getTrimmed(FieldKey.Password)!!
28 | onSubmit(password)
29 | }
30 | }
31 |
32 | internal val SignInConfirmPasswordState.signInMethod: SignInMethod
33 | get() = (this as SignInConfirmPasswordStateImpl).signInMethod
34 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/state/AttemptCounter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.state
17 |
18 | class AttemptCounter {
19 |
20 | fun getCount() = attemptCount
21 |
22 | fun countAttempt() {
23 | val timestamp = System.currentTimeMillis()
24 | if (timestamp - latestAttemptTimeStamp > ATTEMPT_COUNT_RESET_INTERVAL_MS) {
25 | // Reset interval has lapsed so reset the attemptCount
26 | attemptCount = 0
27 | }
28 |
29 | attemptCount += 1
30 | latestAttemptTimeStamp = timestamp
31 | }
32 |
33 | companion object {
34 | const val ATTEMPT_COUNT_RESET_INTERVAL_MS = 300_000L
35 | var attemptCount = 0
36 | var latestAttemptTimeStamp: Long = System.currentTimeMillis()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithMfaSelectionStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import com.amplifyframework.auth.MFAType
4 | import com.amplifyframework.auth.cognito.challengeResponse
5 | import com.amplifyframework.ui.authenticator.SignInContinueWithMfaSelectionState
6 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
7 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
8 | import com.amplifyframework.ui.authenticator.forms.FieldKey
9 |
10 | internal class SignInContinueWithMfaSelectionStateImpl(
11 | override val allowedMfaTypes: Set,
12 | private val onSubmit: suspend (selection: String) -> Unit,
13 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
14 | ) : BaseStateImpl(), SignInContinueWithMfaSelectionState {
15 |
16 | init {
17 | form.addFields {
18 | mfaSelection()
19 | }
20 | form.fields[FieldKey.MfaSelection]?.state?.content = allowedMfaTypes.first().challengeResponse
21 | }
22 |
23 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
24 |
25 | override suspend fun continueSignIn() = doSubmit {
26 | val selected = form.fields[FieldKey.MfaSelection]!!.state.content
27 | onSubmit(selected)
28 | }
29 |
30 | override val step = AuthenticatorStep.SignInContinueWithMfaSelection
31 | }
32 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/ui/LivenessPreviewContainer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.ui
17 |
18 | import androidx.compose.material3.ColorScheme
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Shapes
21 | import androidx.compose.material3.Typography
22 | import androidx.compose.runtime.Composable
23 |
24 | @Composable
25 | internal fun LivenessPreviewContainer(
26 | colorScheme: ColorScheme = LivenessColorScheme.default(),
27 | shapes: Shapes = MaterialTheme.shapes,
28 | typography: Typography = MaterialTheme.typography,
29 | content: @Composable () -> Unit
30 | ) {
31 | MaterialTheme(
32 | colorScheme = colorScheme,
33 | shapes = shapes,
34 | typography = typography
35 | ) {
36 | content()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorTitle.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui
17 |
18 | import androidx.compose.foundation.layout.padding
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Text
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.platform.testTag
24 | import androidx.compose.ui.unit.dp
25 |
26 | /**
27 | * Composable for the title shown at the top of each state's content in Authenticator.
28 | */
29 | @Composable
30 | internal fun AuthenticatorTitle(
31 | text: String,
32 | modifier: Modifier = Modifier
33 | ) {
34 | Text(
35 | style = MaterialTheme.typography.titleLarge,
36 | text = text,
37 | modifier = modifier.padding(bottom = 16.dp).testTag(TestTags.AuthenticatorTitle)
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/.github/workflows/issue_closed.yml:
--------------------------------------------------------------------------------
1 | name: Issue Closed
2 |
3 | on:
4 | issues:
5 | types: [closed]
6 |
7 | permissions:
8 | issues: write
9 |
10 | jobs:
11 | cleanup-labels:
12 | runs-on: ubuntu-latest
13 | if: ${{ (contains(github.event.issue.labels.*.name, 'pending-community-response') || contains(github.event.issue.labels.*.name, 'pending-maintainer-response') || contains(github.event.issue.labels.*.name, 'closing soon') || contains(github.event.issue.labels.*.name, 'pending-release')|| contains(github.event.issue.labels.*.name, 'pending-triage')) }}
14 | steps:
15 | - name: remove unnecessary labels after closing
16 | shell: bash
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 | ISSUE_NUMBER: ${{ github.event.issue.number }}
20 | REPOSITORY_NAME: ${{ github.event.repository.full_name }}
21 | run: |
22 | gh issue edit $ISSUE_NUMBER --repo $REPOSITORY_NAME --remove-label "closing soon" --remove-label "pending-community-response" --remove-label "pending-maintainer-response" --remove-label "pending-release" --remove-label "pending-triage"
23 |
24 | comment-visibility-warning:
25 | runs-on: ubuntu-latest
26 | steps:
27 | - uses: aws-actions/closed-issue-message@36b7048ea77bb834d16e7a7c5b5471ac767a4ca1 # v1
28 | with:
29 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
30 | message: |
31 | This issue is now closed. Comments on closed issues are hard for our team to see.
32 | If you need more assistance, please open a new issue that references this one.
33 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/java/com/amplifyframework/ui/sample/liveness/ui/LivenessScreen.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.sample.liveness.ui
2 |
3 | import androidx.activity.compose.BackHandler
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.runtime.collectAsState
7 | import com.amplifyframework.ui.liveness.model.FaceLivenessDetectionException
8 | import com.amplifyframework.ui.liveness.ui.FaceLivenessDetector
9 | import com.amplifyframework.ui.liveness.ui.LivenessColorScheme
10 | import com.amplifyframework.ui.sample.liveness.MainViewModel
11 |
12 | @Composable
13 | fun LivenessScreen(
14 | viewModel: MainViewModel,
15 | onChallengeComplete: () -> Unit,
16 | onBack: () -> Unit
17 | ) {
18 | BackHandler(onBack = onBack)
19 |
20 | val sessionId = viewModel.sessionId.collectAsState().value ?: return
21 |
22 | MaterialTheme(colorScheme = LivenessColorScheme.default()) {
23 | FaceLivenessDetector(
24 | sessionId = sessionId,
25 | region = "us-east-1",
26 | disableStartView = false,
27 | onComplete = {
28 | viewModel.fetchSessionResult(sessionId)
29 | onChallengeComplete()
30 | },
31 | onError = {
32 | if (it is FaceLivenessDetectionException.UserCancelledException) {
33 | onBack()
34 | } else {
35 | viewModel.reportErrorResult(it)
36 | onChallengeComplete()
37 | }
38 | }
39 | )
40 | }
41 | }
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/java/com/amplifyframework/ui/sample/liveness/LivenessApp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.sample.liveness
17 |
18 | import android.app.Application
19 | import android.util.Log
20 | import com.amplifyframework.AmplifyException
21 | import com.amplifyframework.api.aws.AWSApiPlugin
22 | import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin
23 | import com.amplifyframework.core.Amplify
24 |
25 | class LivenessApp : Application() {
26 |
27 | override fun onCreate() {
28 | super.onCreate()
29 |
30 | try {
31 | Amplify.addPlugin(AWSApiPlugin())
32 | Amplify.addPlugin(AWSCognitoAuthPlugin())
33 | Amplify.configure(applicationContext)
34 | Log.d(TAG, "Initialized Amplify")
35 | } catch (error: AmplifyException) {
36 | Log.e(TAG, "Could not initialize Amplify", error)
37 | }
38 | }
39 |
40 | companion object {
41 | const val TAG = "LivenessApp"
42 | }
43 | }
--------------------------------------------------------------------------------
/authenticator/src/main/res/drawable/ic_authenticator_invisible.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInConfirmTotpCodeRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.junit4.ComposeTestRule
19 | import com.amplifyframework.ui.authenticator.forms.FieldKey
20 | import com.amplifyframework.ui.authenticator.ui.TestTags
21 | import com.amplifyframework.ui.testing.ComposeTest
22 |
23 | fun ComposeTest.signInConfirmTotpCode(func: SignInConfirmTotpCodeRobot.() -> Unit) = SignInConfirmTotpCodeRobot(
24 | composeTestRule
25 | ).func()
26 |
27 | class SignInConfirmTotpCodeRobot(composeTestRule: ComposeTestRule) : ScreenLevelRobot(composeTestRule) {
28 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignInConfirmButton, expected)
29 |
30 | fun setConfirmationCode(code: String) = setFieldContent(FieldKey.ConfirmationCode, code)
31 | fun clickSubmitButton() = clickOnTag(TestTags.SignInConfirmButton)
32 | fun clickBackToSignInButton() = clickOnTag(TestTags.BackToSignInButton)
33 | }
34 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
19 |
20 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/Errors.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */ // ktlint-disable filename
15 |
16 | package com.amplifyframework.ui.authenticator.util
17 |
18 | import com.amplifyframework.auth.cognito.exceptions.service.InvalidPasswordException
19 | import com.amplifyframework.ui.authenticator.forms.FieldError
20 |
21 | // This SHOULD be caught by the field validator, but it's possible that the local configuration is
22 | // different from the server configuration. In such cases this exception will be returned.
23 | // We cannot extract the specific problem from the exception, so just show the message returned
24 | // or an InvalidFormat if none
25 | internal fun InvalidPasswordException.toFieldError(): FieldError {
26 | val cause = this.cause
27 | return if (
28 | cause is aws.sdk.kotlin.services.cognitoidentityprovider.model.InvalidPasswordException &&
29 | cause.localizedMessage != null
30 | ) {
31 | FieldError.Custom(cause.localizedMessage!!)
32 | } else {
33 | FieldError.InvalidFormat
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithEmailSetupRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.junit4.ComposeTestRule
19 | import com.amplifyframework.ui.authenticator.forms.FieldKey
20 | import com.amplifyframework.ui.authenticator.ui.TestTags
21 | import com.amplifyframework.ui.testing.ComposeTest
22 |
23 | fun ComposeTest.signInContinueWithEmailSetup(func: SignInContinueWithEmailSetupRobot.() -> Unit) =
24 | SignInContinueWithEmailSetupRobot(composeTestRule).func()
25 |
26 | class SignInContinueWithEmailSetupRobot(composeTestRule: ComposeTestRule) : ScreenLevelRobot(composeTestRule) {
27 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignInConfirmButton, expected)
28 |
29 | fun setEmail(email: String) = setFieldContent(FieldKey.Email, email)
30 |
31 | fun clickSubmitButton() = clickOnTag(TestTags.SignInConfirmButton)
32 |
33 | fun clickBackToSignInButton() = clickOnTag(TestTags.BackToSignInButton)
34 | }
35 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/model/Freshness.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.model
17 |
18 | import androidx.compose.ui.graphics.Color
19 | import com.amplifyframework.predictions.aws.models.ColorDisplayInformation
20 | import com.amplifyframework.predictions.aws.models.RgbColor
21 |
22 | internal fun RgbColor.toComposeColor(alpha: Int) =
23 | Color(
24 | red = red,
25 | green = green,
26 | blue = blue,
27 | alpha = alpha
28 | )
29 |
30 | internal sealed class SceneType {
31 | object DownScroll : SceneType()
32 | object Flat : SceneType()
33 | }
34 |
35 | internal data class FreshnessColorScene(
36 | val startTime: Long,
37 | val endTime: Long,
38 | val currentColor: ColorDisplayInformation,
39 | val previousColor: ColorDisplayInformation?,
40 | val sceneType: SceneType
41 | )
42 |
43 | internal data class FreshnessColorFrame(
44 | val sceneType: SceneType,
45 | val currentColor: Color,
46 | val previousColor: Color?,
47 | val sceneCompletionPercentage: Float
48 | )
49 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/java/com/amplifyframework/ui/sample/authenticator/AuthenticatorSampleApp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.sample.authenticator
17 |
18 | import android.app.Application
19 | import android.util.Log
20 | import com.amplifyframework.AmplifyException
21 | import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin
22 | import com.amplifyframework.core.Amplify
23 | import com.amplifyframework.logging.AndroidLoggingPlugin
24 | import com.amplifyframework.logging.LogLevel
25 |
26 | class AuthenticatorSampleApp : Application() {
27 | override fun onCreate() {
28 | super.onCreate()
29 | try {
30 | Amplify.addPlugin(AndroidLoggingPlugin(LogLevel.VERBOSE))
31 | Amplify.addPlugin(AWSCognitoAuthPlugin())
32 | Amplify.configure(applicationContext)
33 | } catch (e: AmplifyException) {
34 | Log.e("AuthenticatorSampleApp", "Cannot instantiate Amplify. Please see the getting started guide at https://docs.amplify.aws/lib/auth/getting-started/q/platform/android/ for details.", e)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/DividerWithText.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material3.HorizontalDivider
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.text.TextStyle
13 | import androidx.compose.ui.unit.Dp
14 | import androidx.compose.ui.unit.dp
15 |
16 | @Composable
17 | internal fun DividerWithText(
18 | text: String,
19 | modifier: Modifier = Modifier,
20 | textStyle: TextStyle = MaterialTheme.typography.bodyMedium,
21 | textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
22 | dividerColor: Color = MaterialTheme.colorScheme.outline,
23 | thickness: Dp = 1.dp,
24 | textPadding: Dp = 16.dp
25 | ) {
26 | Row(
27 | modifier = modifier,
28 | verticalAlignment = Alignment.CenterVertically
29 | ) {
30 | HorizontalDivider(
31 | modifier = Modifier.weight(1f),
32 | color = dividerColor,
33 | thickness = thickness
34 | )
35 | Text(
36 | text = text,
37 | style = textStyle,
38 | color = textColor,
39 | modifier = Modifier.padding(horizontal = textPadding)
40 | )
41 | HorizontalDivider(
42 | modifier = Modifier.weight(1f),
43 | color = dividerColor,
44 | thickness = thickness
45 | )
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithTotpSetupRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.junit4.ComposeTestRule
19 | import com.amplifyframework.ui.authenticator.forms.FieldKey
20 | import com.amplifyframework.ui.authenticator.ui.TestTags
21 | import com.amplifyframework.ui.testing.ComposeTest
22 |
23 | fun ComposeTest.signInContinueWithTotpSetup(func: SignInContinueWithTotpSetupRobot.() -> Unit) =
24 | SignInContinueWithTotpSetupRobot(composeTestRule).func()
25 |
26 | class SignInContinueWithTotpSetupRobot(composeTestRule: ComposeTestRule) : ScreenLevelRobot(composeTestRule) {
27 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignInConfirmButton, expected)
28 |
29 | fun setConfirmationCode(code: String) = setFieldContent(FieldKey.ConfirmationCode, code)
30 | fun clickSubmitButton() = clickOnTag(TestTags.SignInConfirmButton)
31 | fun clickBackToSignInButton() = clickOnTag(TestTags.BackToSignInButton)
32 | fun clickCopyKeyButton() = clickOnTag(TestTags.CopyKeyButton)
33 | }
34 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/data/PasskeyPrompt.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.data
17 |
18 | /**
19 | * Class that contains configuration values for when/if to show prompts to create passkeys to the user.
20 | */
21 | data class PasskeyPrompts(
22 | /**
23 | * Show a prompt after a user who does not have a passkey registered signs in to the application.
24 | */
25 | val afterSignIn: PasskeyPrompt = PasskeyPrompt.Always,
26 | /**
27 | * Show a prompt to create a passkey after the automatic sign in following a new user signing up.
28 | */
29 | val afterSignUp: PasskeyPrompt = PasskeyPrompt.Always
30 | )
31 |
32 | /**
33 | * Possible selections for controlling passkey prompts.
34 | */
35 | sealed interface PasskeyPrompt {
36 | /**
37 | * Never prompt users to create a passkey after signing in.
38 | */
39 | data object Never : PasskeyPrompt
40 |
41 | /**
42 | * Always prompt users to create a passkey after signing in if they don't already have an existing registered
43 | * passkey.
44 | */
45 | data object Always : PasskeyPrompt
46 | }
47 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithEmailSetupStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.ui.authenticator.SignInContinueWithEmailSetupState
19 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
20 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
21 | import com.amplifyframework.ui.authenticator.forms.FieldKey
22 |
23 | internal class SignInContinueWithEmailSetupStateImpl(
24 | private val onSubmit: suspend (email: String) -> Unit,
25 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
26 | ) : BaseStateImpl(), SignInContinueWithEmailSetupState {
27 |
28 | init {
29 | form.addFields {
30 | email(required = true)
31 | }
32 | }
33 |
34 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
35 |
36 | override suspend fun continueSignIn() = doSubmit {
37 | val email = form.getTrimmed(FieldKey.Email)!!
38 | onSubmit(email)
39 | }
40 |
41 | override val step = AuthenticatorStep.SignInContinueWithEmailSetup
42 | }
43 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/util/Extensions.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.util
17 |
18 | import android.Manifest
19 | import android.app.Activity
20 | import android.content.Context
21 | import android.content.ContextWrapper
22 | import android.content.pm.PackageManager
23 | import android.media.MediaCodec
24 | import android.view.Display
25 | import android.view.Surface
26 | import androidx.core.content.ContextCompat
27 |
28 | internal fun MediaCodec.BufferInfo.isKeyFrame() =
29 | flags.and(MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0
30 |
31 | internal fun Context.hasCameraPermission() =
32 | ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
33 | PackageManager.PERMISSION_GRANTED
34 |
35 | internal fun Context.findActivity(): Activity? = when (this) {
36 | is Activity -> this
37 | is ContextWrapper -> baseContext.findActivity()
38 | else -> null
39 | }
40 |
41 | internal fun Display.rotationDegrees() = when (rotation) {
42 | Surface.ROTATION_0 -> 0
43 | Surface.ROTATION_90 -> 90
44 | Surface.ROTATION_180 -> 180
45 | Surface.ROTATION_270 -> 270
46 | else -> 0
47 | }
48 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/VerifyUserStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.auth.AuthUserAttribute
19 | import com.amplifyframework.auth.AuthUserAttributeKey
20 | import com.amplifyframework.ui.authenticator.VerifyUserState
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
22 | import com.amplifyframework.ui.authenticator.forms.FieldKey
23 |
24 | internal class VerifyUserStateImpl(
25 | override val attributes: List,
26 | private val onSubmit: suspend (attribute: AuthUserAttributeKey) -> Unit,
27 | private val onSkip: () -> Unit
28 | ) : BaseStateImpl(), VerifyUserState {
29 |
30 | init {
31 | form.addFields {
32 | verificationAttribute()
33 | }
34 | }
35 |
36 | override val step = AuthenticatorStep.VerifyUser
37 |
38 | override suspend fun verifyUser() = doSubmit {
39 | val keyString = form.getTrimmed(FieldKey.VerificationAttribute)!!
40 | val key = AuthUserAttributeKey.custom(keyString)
41 | onSubmit(key)
42 | }
43 |
44 | override fun skip() = onSkip()
45 | }
46 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/Autofill.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.util
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.ReadOnlyComposable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.autofill.ContentType
7 | import androidx.compose.ui.semantics.contentType
8 | import androidx.compose.ui.semantics.semantics
9 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
10 | import com.amplifyframework.ui.authenticator.forms.FieldKey
11 | import com.amplifyframework.ui.authenticator.locals.LocalAuthenticatorStep
12 |
13 | @Composable
14 | @ReadOnlyComposable
15 | internal fun Modifier.contentTypeForKey(key: FieldKey): Modifier {
16 | val derivedContentType = key.deriveContentType()
17 | return this.semantics { if (derivedContentType != null) contentType = derivedContentType }
18 | }
19 |
20 | @Composable
21 | @ReadOnlyComposable
22 | internal fun FieldKey.deriveContentType(): ContentType? {
23 | val step = LocalAuthenticatorStep.current
24 | return when (this) {
25 | is FieldKey.Username -> if (step.isNewUsername()) ContentType.NewUsername else ContentType.Username
26 | is FieldKey.Password -> if (step.isNewPassword()) ContentType.NewPassword else ContentType.Password
27 | is FieldKey.ConfirmPassword -> ContentType.NewPassword
28 | is FieldKey.ConfirmationCode -> ContentType.SmsOtpCode
29 | is FieldKey.Email -> ContentType.EmailAddress
30 | is FieldKey.PhoneNumber -> ContentType.PhoneNumber
31 | else -> null
32 | }
33 | }
34 |
35 | private fun AuthenticatorStep.isNewUsername() = this is AuthenticatorStep.SignUp
36 | private fun AuthenticatorStep.isNewPassword() =
37 | this is AuthenticatorStep.SignUp || this is AuthenticatorStep.PasswordResetConfirm
38 |
--------------------------------------------------------------------------------
/authenticator/src/main/res/values/errors.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 | Username or Password is incorrect
19 |
20 | Passkey creation failed
21 |
22 | User password cannot be reset in the current state
23 | Could not send verification code
24 | Code has expired
25 | Please check your connectivity
26 | You\'ve reached the request limit. Please try again later.
27 | Your session has expired. Please try to sign in again.
28 | Sorry, something went wrong
29 |
30 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/VerifyUserConfirmStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.auth.AuthCodeDeliveryDetails
19 | import com.amplifyframework.ui.authenticator.VerifyUserConfirmState
20 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
21 | import com.amplifyframework.ui.authenticator.forms.FieldKey
22 |
23 | internal class VerifyUserConfirmStateImpl(
24 | override val deliveryDetails: AuthCodeDeliveryDetails?,
25 | private val onSubmit: suspend (confirmationCode: String) -> Unit,
26 | private val onResendCode: suspend () -> Unit,
27 | private val onSkip: () -> Unit
28 | ) : BaseStateImpl(), VerifyUserConfirmState {
29 |
30 | init {
31 | form.addFields {
32 | confirmationCode()
33 | }
34 | }
35 |
36 | override val step = AuthenticatorStep.VerifyUserConfirm
37 | override suspend fun confirmVerifyUser() = doSubmit {
38 | val confirmationCode = form.getTrimmed(FieldKey.ConfirmationCode)!!
39 | onSubmit(confirmationCode)
40 | }
41 |
42 | override suspend fun resendCode() = onResendCode()
43 | override fun skip() = onSkip()
44 | }
45 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/PasswordResetStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.ui.authenticator.PasswordResetState
19 | import com.amplifyframework.ui.authenticator.auth.SignInMethod
20 | import com.amplifyframework.ui.authenticator.auth.toFieldKey
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
22 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
23 |
24 | internal class PasswordResetStateImpl(
25 | private val signInMethod: SignInMethod,
26 | private val onSubmit: suspend (username: String) -> Unit,
27 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
28 | ) : BaseStateImpl(), PasswordResetState {
29 |
30 | init {
31 | form.addFields {
32 | fieldForSignInMethod(signInMethod)
33 | }
34 | }
35 |
36 | override val step = AuthenticatorStep.PasswordReset
37 |
38 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
39 |
40 | override suspend fun submitPasswordReset() = doSubmit {
41 | val username = form.getTrimmed(signInMethod.toFieldKey())!!
42 | onSubmit(username)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/java/com/amplifyframework/ui/sample/authenticator/theme/default/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.sample.authenticator.theme.default
17 |
18 | import androidx.compose.material3.Typography
19 | import androidx.compose.ui.text.TextStyle
20 | import androidx.compose.ui.text.font.FontFamily
21 | import androidx.compose.ui.text.font.FontWeight
22 | import androidx.compose.ui.unit.sp
23 |
24 | // Set of Material typography styles to start with
25 | val Typography = Typography(
26 | bodyLarge = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Normal,
29 | fontSize = 16.sp,
30 | lineHeight = 24.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | /* Other default text styles to override
34 | titleLarge = TextStyle(
35 | fontFamily = FontFamily.Default,
36 | fontWeight = FontWeight.Normal,
37 | fontSize = 22.sp,
38 | lineHeight = 28.sp,
39 | letterSpacing = 0.sp
40 | ),
41 | labelSmall = TextStyle(
42 | fontFamily = FontFamily.Default,
43 | fontWeight = FontWeight.Medium,
44 | fontSize = 11.sp,
45 | lineHeight = 16.sp,
46 | letterSpacing = 0.5.sp
47 | )
48 | */
49 | )
50 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInConfirmMfaStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.auth.AuthCodeDeliveryDetails
19 | import com.amplifyframework.ui.authenticator.SignInConfirmMfaState
20 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
22 | import com.amplifyframework.ui.authenticator.forms.FieldKey
23 |
24 | internal class SignInConfirmMfaStateImpl(
25 | override val deliveryDetails: AuthCodeDeliveryDetails?,
26 | private val onSubmit: suspend (confirmationCode: String) -> Unit,
27 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
28 | ) : BaseStateImpl(),
29 | SignInConfirmMfaState {
30 |
31 | init {
32 | form.addFields {
33 | confirmationCode()
34 | }
35 | }
36 |
37 | override val step = AuthenticatorStep.SignInConfirmMfa
38 |
39 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
40 |
41 | override suspend fun confirmSignIn() = doSubmit {
42 | val confirmationCode = form.getTrimmed(FieldKey.ConfirmationCode)!!
43 | onSubmit(confirmationCode)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/ui/AlwaysOnMaxBrightnessScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.ui
17 |
18 | import android.view.WindowManager
19 | import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
20 | import androidx.compose.runtime.Composable
21 | import androidx.compose.runtime.DisposableEffect
22 | import androidx.compose.ui.platform.LocalContext
23 | import com.amplifyframework.ui.liveness.util.findActivity
24 |
25 | @Composable
26 | internal fun AlwaysOnMaxBrightnessScreen() {
27 | val context = LocalContext.current
28 | DisposableEffect(Unit) {
29 | val activity = context.findActivity() ?: return@DisposableEffect onDispose {}
30 | val originalBrightness = activity.window.attributes.screenBrightness
31 | activity.window.addFlags(FLAG_KEEP_SCREEN_ON)
32 | activity.window.attributes = activity.window.attributes.apply {
33 | screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
34 | }
35 | onDispose {
36 | activity.window.clearFlags(FLAG_KEEP_SCREEN_ON)
37 | activity.window.attributes = activity.window.attributes.apply {
38 | screenBrightness = originalBrightness
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/java/com/amplifyframework/ui/sample/liveness/LivenessSampleBackend.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.sample.liveness
17 |
18 | import com.amplifyframework.api.rest.RestOptions
19 | import com.amplifyframework.kotlin.core.Amplify
20 | import kotlinx.serialization.Serializable
21 | import kotlinx.serialization.decodeFromString
22 | import kotlinx.serialization.json.Json
23 |
24 | object LivenessSampleBackend {
25 |
26 | private val json = Json { ignoreUnknownKeys = true }
27 |
28 | suspend fun createSession(): String {
29 | val request = RestOptions.builder()
30 | .addPath("/liveness/create")
31 | .build()
32 |
33 | return Amplify.API.post(request).data.asJSONObject()["sessionId"] as String
34 | }
35 |
36 | suspend fun getLivenessSessionResults(sessionId: String): LivenessSessionResult {
37 | val request = RestOptions.builder()
38 | .addPath("/liveness/$sessionId")
39 | .build()
40 |
41 | val result = Amplify.API.get(request)
42 | return json.decodeFromString(result.data.asString())
43 | }
44 | }
45 |
46 | @Serializable
47 | data class LivenessSessionResult(
48 | val confidenceScore: Float,
49 | val isLive: Boolean,
50 | val auditImageBytes: String
51 | )
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInConfirmCustomStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.auth.AuthCodeDeliveryDetails
19 | import com.amplifyframework.ui.authenticator.SignInConfirmCustomState
20 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
22 | import com.amplifyframework.ui.authenticator.forms.FieldKey
23 |
24 | internal class SignInConfirmCustomStateImpl(
25 | override val deliveryDetails: AuthCodeDeliveryDetails?,
26 | override val additionalInfo: Map,
27 | private val onSubmit: suspend (confirmationCode: String) -> Unit,
28 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
29 | ) : BaseStateImpl(), SignInConfirmCustomState {
30 |
31 | init {
32 | form.addFields {
33 | confirmationCode()
34 | }
35 | }
36 |
37 | override val step = AuthenticatorStep.SignInConfirmCustomAuth
38 |
39 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
40 |
41 | override suspend fun confirmSignIn() = doSubmit {
42 | val confirmationCode = form.getTrimmed(FieldKey.ConfirmationCode)!!
43 | onSubmit(confirmationCode)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignUpConfirmStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.auth.AuthCodeDeliveryDetails
19 | import com.amplifyframework.ui.authenticator.SignUpConfirmState
20 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
22 | import com.amplifyframework.ui.authenticator.forms.FieldKey
23 |
24 | internal data class SignUpConfirmStateImpl(
25 | override val deliveryDetails: AuthCodeDeliveryDetails?,
26 | private val onSubmit: suspend (confirmationCode: String) -> Unit,
27 | private val onResendCode: suspend () -> Unit,
28 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
29 | ) : BaseStateImpl(), SignUpConfirmState {
30 |
31 | init {
32 | form.addFields {
33 | confirmationCode()
34 | }
35 | }
36 |
37 | override val step = AuthenticatorStep.SignUpConfirm
38 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
39 |
40 | override suspend fun confirmSignUp() = doSubmit {
41 | val confirmationCode = form.getTrimmed(FieldKey.ConfirmationCode)!!
42 | onSubmit(confirmationCode)
43 | }
44 |
45 | override suspend fun resendCode() = onResendCode()
46 | }
47 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfaTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui
17 |
18 | import com.amplifyframework.ui.authenticator.forms.FieldError
19 | import com.amplifyframework.ui.authenticator.forms.FieldKey
20 | import com.amplifyframework.ui.authenticator.forms.setFieldError
21 | import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
22 | import com.amplifyframework.ui.authenticator.testUtil.mockSignInConfirmMfaState
23 | import com.amplifyframework.ui.authenticator.ui.robots.signInConfirmMfa
24 | import com.amplifyframework.ui.testing.ScreenshotTest
25 | import org.junit.Test
26 |
27 | class SignInConfirmMfaTest : AuthenticatorUiTest() {
28 | @Test
29 | @ScreenshotTest
30 | fun `default state`() {
31 | setContent {
32 | SignInConfirmMfa(
33 | mockSignInConfirmMfaState()
34 | )
35 | }
36 | }
37 |
38 | @Test
39 | @ScreenshotTest
40 | fun `incorrect code`() {
41 | val state = mockSignInConfirmMfaState()
42 | setContent {
43 | SignInConfirmMfa(state)
44 | }
45 |
46 | signInConfirmMfa {
47 | setConfirmationCode("123456")
48 | }
49 |
50 | state.form.setFieldError(FieldKey.ConfirmationCode, FieldError.ConfirmationCodeIncorrect)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.autofill.ContentType
19 | import androidx.compose.ui.test.junit4.ComposeTestRule
20 | import com.amplifyframework.ui.authenticator.forms.FieldKey
21 | import com.amplifyframework.ui.authenticator.ui.TestTags
22 | import com.amplifyframework.ui.testing.ComposeTest
23 |
24 | fun ComposeTest.signIn(func: SignInRobot.() -> Unit) = SignInRobot(composeTestRule).func()
25 |
26 | class SignInRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
27 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignInButton, expected)
28 | fun hasUsernameContentType(contentType: ContentType) = hasContentType(FieldKey.Username, contentType)
29 | fun hasPasswordContentType(contentType: ContentType) = hasContentType(FieldKey.Password, contentType)
30 |
31 | fun setUsername(value: String) = setFieldContent(FieldKey.Username, value)
32 | fun setPassword(value: String) = setFieldContent(FieldKey.Password, value)
33 | fun clickShowPassword() = clickOnShowIcon(FieldKey.Password)
34 | fun clickSubmitButton() = clickOnTag(TestTags.SignInButton)
35 | fun clickForgotPassword() = clickOnTag(TestTags.ForgotPasswordButton)
36 | fun clickCreateAccount() = clickOnTag(TestTags.CreateAccountButton)
37 | }
38 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInConfirmNewPasswordStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.ui.authenticator.SignInConfirmNewPasswordState
19 | import com.amplifyframework.ui.authenticator.auth.PasswordCriteria
20 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
22 | import com.amplifyframework.ui.authenticator.forms.FieldKey
23 | import com.amplifyframework.ui.authenticator.forms.FieldValidators
24 |
25 | internal class SignInConfirmNewPasswordStateImpl(
26 | passwordCriteria: PasswordCriteria,
27 | private val onSubmit: suspend (password: String) -> Unit,
28 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
29 | ) : BaseStateImpl(), SignInConfirmNewPasswordState {
30 |
31 | init {
32 | form.addFields {
33 | password(validator = FieldValidators.password(passwordCriteria))
34 | }
35 | }
36 |
37 | override val step: AuthenticatorStep = AuthenticatorStep.SignInConfirmNewPassword
38 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
39 |
40 | override suspend fun confirmSignIn() = doSubmit {
41 | val password = form.getTrimmed(FieldKey.Password)!!
42 | onSubmit(password)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithMfaSelectionRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.hasParent
19 | import androidx.compose.ui.test.hasTestTag
20 | import androidx.compose.ui.test.isSelected
21 | import androidx.compose.ui.test.junit4.ComposeTestRule
22 | import com.amplifyframework.auth.MFAType
23 | import com.amplifyframework.auth.cognito.challengeResponse
24 | import com.amplifyframework.ui.authenticator.ui.TestTags
25 | import com.amplifyframework.ui.testing.ComposeTest
26 |
27 | fun ComposeTest.signInContinueWithMfaSelection(func: SignInContinueWithMfaSelectionRobot.() -> Unit) =
28 | SignInContinueWithMfaSelectionRobot(composeTestRule).func()
29 |
30 | class SignInContinueWithMfaSelectionRobot(composeTestRule: ComposeTestRule) : ScreenLevelRobot(composeTestRule) {
31 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignInConfirmButton, expected)
32 | fun hasMfaTypeSelected(expected: MFAType) {
33 | composeTestRule.onNode(hasParent(hasTestTag(expected.challengeResponse)) and isSelected()).assertExists()
34 | }
35 |
36 | fun selectMfaType(type: MFAType) = clickOnTag(type.challengeResponse)
37 | fun clickSubmitButton() = clickOnTag(TestTags.SignInConfirmButton)
38 | fun clickBackToSignInButton() = clickOnTag(TestTags.BackToSignInButton)
39 | }
40 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/ui/LockPortraitOrientation.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.ui
17 |
18 | import android.annotation.SuppressLint
19 | import android.content.pm.ActivityInfo
20 | import android.content.res.Configuration.ORIENTATION_PORTRAIT
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.runtime.SideEffect
23 | import androidx.compose.runtime.getValue
24 | import androidx.compose.runtime.mutableStateOf
25 | import androidx.compose.runtime.saveable.rememberSaveable
26 | import androidx.compose.ui.platform.LocalContext
27 | import com.amplifyframework.ui.liveness.util.findActivity
28 |
29 | @SuppressLint("SourceLockedOrientationActivity")
30 | @Composable
31 | internal fun LockPortraitOrientation(content: @Composable (resetOrientation: () -> Unit) -> Unit) {
32 | val context = LocalContext.current
33 | val activity = context.findActivity() ?: return content {}
34 | val originalOrientation by rememberSaveable { mutableStateOf(activity.requestedOrientation) }
35 | SideEffect {
36 | activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
37 | }
38 |
39 | // wait until screen is rotated correctly
40 | if (activity.resources.configuration.orientation == ORIENTATION_PORTRAIT) {
41 | content {
42 | activity.requestedOrientation = originalOrientation
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/testing/src/main/java/com/amplifyframework/ui/testing/CoroutineTestRule.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.testing
17 |
18 | import kotlinx.coroutines.Dispatchers
19 | import kotlinx.coroutines.ExperimentalCoroutinesApi
20 | import kotlinx.coroutines.test.StandardTestDispatcher
21 | import kotlinx.coroutines.test.TestDispatcher
22 | import kotlinx.coroutines.test.resetMain
23 | import kotlinx.coroutines.test.setMain
24 | import org.junit.rules.TestWatcher
25 | import org.junit.runner.Description
26 |
27 | /**
28 | * A JUnit rule that overrides the main coroutine dispatcher in tests.
29 | *
30 | * See https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test for information on how to use
31 | * a TestDispatcher to test code that launches coroutines.
32 | *
33 | * Usage:
34 | * @get:Rule
35 | * val coroutineRule = CoroutineTestRule()
36 | * @Test
37 | * fun myTest() = runTest {
38 | * // ... test code here
39 | * }
40 | */
41 | @OptIn(ExperimentalCoroutinesApi::class)
42 | class CoroutineTestRule(private val dispatcher: TestDispatcher = StandardTestDispatcher()) : TestWatcher() {
43 | override fun starting(description: Description) {
44 | super.starting(description)
45 | Dispatchers.setMain(dispatcher)
46 | }
47 |
48 | override fun finished(description: Description) {
49 | super.finished(description)
50 | Dispatchers.resetMain()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorError.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui
17 |
18 | import androidx.compose.foundation.background
19 | import androidx.compose.foundation.layout.Box
20 | import androidx.compose.foundation.layout.fillMaxWidth
21 | import androidx.compose.foundation.layout.padding
22 | import androidx.compose.material3.MaterialTheme
23 | import androidx.compose.material3.Text
24 | import androidx.compose.runtime.Composable
25 | import androidx.compose.ui.Alignment
26 | import androidx.compose.ui.Modifier
27 | import androidx.compose.ui.unit.dp
28 | import com.amplifyframework.ui.authenticator.ErrorState
29 | import com.amplifyframework.ui.authenticator.strings.StringResolver
30 |
31 | /**
32 | * The content displayed when Authenticator is in the ErrorState
33 | */
34 | @Composable
35 | fun AuthenticatorError(
36 | state: ErrorState,
37 | modifier: Modifier = Modifier
38 | ) {
39 | Box(
40 | modifier = modifier
41 | .fillMaxWidth()
42 | .padding(16.dp)
43 | .background(MaterialTheme.colorScheme.errorContainer)
44 | .padding(16.dp),
45 | contentAlignment = Alignment.Center
46 | ) {
47 | val message = StringResolver.error(state.error)
48 | Text(
49 | text = message,
50 | color = MaterialTheme.colorScheme.onErrorContainer
51 | )
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/samples/liveness/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License").
5 | # You may not use this file except in compliance with the License.
6 | # A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/apache2.0
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed
11 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | # express or implied. See the License for the specific language governing
13 | # permissions and limitations under the License.
14 | #
15 |
16 | # Project-wide Gradle settings.
17 | # IDE (e.g. Android Studio) users:
18 | # Gradle settings configured through the IDE *will override*
19 | # any settings specified in this file.
20 | # For more details on how to configure your build environment visit
21 | # http://www.gradle.org/docs/current/userguide/build_environment.html
22 | # Specifies the JVM arguments used for the daemon process.
23 | # The setting is particularly useful for tweaking memory settings.
24 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
25 | # When configured, Gradle will run in incubating parallel mode.
26 | # This option should only be used with decoupled projects. More details, visit
27 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
28 | # org.gradle.parallel=true
29 | # AndroidX package structure to make it clearer which packages are bundled with the
30 | # Android operating system, and which are packaged with your app's APK
31 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
32 | android.useAndroidX=true
33 | # Kotlin code style for this project: "official" or "obsolete":
34 | kotlin.code.style=official
35 | # Enables namespacing of each library's R class so that its R class includes only the
36 | # resources declared in the library itself and none from the library's dependencies,
37 | # thereby reducing the size of the R class for that library
38 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/samples/authenticator/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License").
5 | # You may not use this file except in compliance with the License.
6 | # A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/apache2.0
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed
11 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | # express or implied. See the License for the specific language governing
13 | # permissions and limitations under the License.
14 | #
15 |
16 | # Project-wide Gradle settings.
17 | # IDE (e.g. Android Studio) users:
18 | # Gradle settings configured through the IDE *will override*
19 | # any settings specified in this file.
20 | # For more details on how to configure your build environment visit
21 | # http://www.gradle.org/docs/current/userguide/build_environment.html
22 | # Specifies the JVM arguments used for the daemon process.
23 | # The setting is particularly useful for tweaking memory settings.
24 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
25 | # When configured, Gradle will run in incubating parallel mode.
26 | # This option should only be used with decoupled projects. More details, visit
27 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
28 | # org.gradle.parallel=true
29 | # AndroidX package structure to make it clearer which packages are bundled with the
30 | # Android operating system, and which are packaged with your app's APK
31 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
32 | android.useAndroidX=true
33 | # Kotlin code style for this project: "official" or "obsolete":
34 | kotlin.code.style=official
35 | # Enables namespacing of each library's R class so that its R class includes only the
36 | # resources declared in the library itself and none from the library's dependencies,
37 | # thereby reducing the size of the R class for that library
38 | android.nonTransitiveRClass=true
39 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInConfirmPasswordRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.hasTestTag
19 | import androidx.compose.ui.test.hasText
20 | import androidx.compose.ui.test.junit4.ComposeTestRule
21 | import com.amplifyframework.ui.authenticator.forms.FieldKey
22 | import com.amplifyframework.ui.authenticator.ui.TestTags
23 | import com.amplifyframework.ui.authenticator.ui.testTag
24 | import com.amplifyframework.ui.testing.ComposeTest
25 |
26 | fun ComposeTest.signInConfirmPassword(func: SignInConfirmPasswordRobot.() -> Unit) =
27 | SignInConfirmPasswordRobot(composeTestRule).func()
28 |
29 | class SignInConfirmPasswordRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
30 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignInButton, expected)
31 | fun hasUsername(expected: String) = composeTestRule.onNode(
32 | hasTestTag(FieldKey.Username.testTag) and hasText(expected)
33 | ).assertExists()
34 | fun hasBackToSignInButton() = composeTestRule.onNode(hasTestTag(TestTags.BackToSignInButton)).assertExists()
35 |
36 | fun setPassword(value: String) = setFieldContent(FieldKey.Password, value)
37 | fun clickShowPassword() = clickOnShowIcon(FieldKey.Password)
38 | fun clickSubmitButton() = clickOnTag(TestTags.SignInButton)
39 | fun clickBackToSignIn() = clickOnTag(TestTags.BackToSignInButton)
40 | }
41 |
--------------------------------------------------------------------------------
/samples/authenticator/app/src/main/java/com/amplifyframework/ui/sample/authenticator/data/ThemeDatastore.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.sample.authenticator.data
2 |
3 | import android.content.Context
4 | import androidx.datastore.core.DataStore
5 | import androidx.datastore.preferences.core.Preferences
6 | import androidx.datastore.preferences.core.booleanPreferencesKey
7 | import androidx.datastore.preferences.core.edit
8 | import androidx.datastore.preferences.core.stringPreferencesKey
9 | import androidx.datastore.preferences.preferencesDataStore
10 | import com.amplifyframework.ui.sample.authenticator.SupportedTheme
11 | import kotlinx.coroutines.flow.Flow
12 | import kotlinx.coroutines.flow.map
13 |
14 | class ThemeDatastore(context: Context) {
15 |
16 | private val datastore = initDatastore(context)
17 |
18 | private val darkModeKey = booleanPreferencesKey("darkMode")
19 | private val themeKey = stringPreferencesKey("theme")
20 |
21 | suspend fun saveTheme(theme: SupportedTheme) {
22 | datastore.edit { settings ->
23 | settings[themeKey] = theme.name
24 | }
25 | }
26 |
27 | suspend fun saveDarkMode(darkMode: Boolean) {
28 | datastore.edit { settings ->
29 | settings[darkModeKey] = darkMode
30 | }
31 | }
32 |
33 | val darkMode: Flow = datastore.data.map { settings ->
34 | settings[darkModeKey] ?: false
35 | }
36 | val theme: Flow = datastore.data.map { settings ->
37 | val name = settings[themeKey] ?: SupportedTheme.Default.name
38 | SupportedTheme.valueOf(name)
39 | }
40 |
41 | companion object {
42 | private val Context.dataStore: DataStore by preferencesDataStore(name = "theme")
43 |
44 | private lateinit var dataStore: DataStore
45 |
46 | private fun initDatastore(context: Context): DataStore {
47 | if (!::dataStore.isInitialized) {
48 | dataStore = context.dataStore
49 | }
50 | return dataStore
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import com.amplifyframework.ui.authenticator.SignInState
19 | import com.amplifyframework.ui.authenticator.auth.SignInMethod
20 | import com.amplifyframework.ui.authenticator.auth.toFieldKey
21 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
22 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
23 | import com.amplifyframework.ui.authenticator.forms.FieldKey
24 |
25 | internal class SignInStateImpl(
26 | private val signInMethod: SignInMethod,
27 | showPasswordField: Boolean,
28 | private val onSubmit: suspend (username: String, password: String?) -> Unit,
29 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
30 | ) : BaseStateImpl(), SignInState {
31 |
32 | init {
33 | form.addFields {
34 | fieldForSignInMethod(signInMethod)
35 | if (showPasswordField) {
36 | password()
37 | }
38 | }
39 | }
40 |
41 | override val step = AuthenticatorStep.SignIn
42 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
43 |
44 | override suspend fun signIn() = doSubmit {
45 | val username = form.getTrimmed(signInMethod.toFieldKey())!!
46 | val password = form.getTrimmed(FieldKey.Password)
47 | onSubmit(username, password)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/liveness/src/main/java/com/amplifyframework/ui/liveness/ml/FaceOval.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.liveness.ml
17 |
18 | import android.graphics.RectF
19 | import com.amplifyframework.predictions.aws.models.FaceTargetChallenge
20 | import kotlin.math.max
21 |
22 | internal object FaceOval {
23 |
24 | fun createBoundingRect(
25 | ovalInfo: FaceTargetChallenge
26 | ): RectF {
27 | val left = ovalInfo.targetCenterX - (ovalInfo.targetWidth / 2)
28 | val top = ovalInfo.targetCenterY - (ovalInfo.targetHeight / 2)
29 | val right = left + ovalInfo.targetWidth
30 | val bottom = top + ovalInfo.targetHeight
31 | return RectF(left, top, right, bottom)
32 | }
33 |
34 | // Creates a new rectangle that is a mirror of the given rectangle
35 | fun convertMirroredRectangle(rectangle: RectF, fullViewWidth: Int): RectF {
36 | val newLeft = max(0f, fullViewWidth - 1 - rectangle.right)
37 | val newRight = fullViewWidth - 1 - rectangle.left
38 | val newTop = rectangle.top
39 | val newBottom = rectangle.bottom
40 | return RectF(newLeft, newTop, newRight, newBottom)
41 | }
42 |
43 | fun convertMirroredLandmark(
44 | landmark: FaceDetector.Landmark,
45 | fullViewWidth: Int
46 | ): FaceDetector.Landmark {
47 | val newX = max(0f, fullViewWidth - 1 - landmark.x)
48 | val newY = landmark.y
49 | return FaceDetector.Landmark(newX, newY)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithMfaSetupSelectionStateImpl.kt:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License").
6 | * You may not use this file except in compliance with the License.
7 | * A copy of the License is located at
8 | *
9 | * http://aws.amazon.com/apache2.0
10 | *
11 | * or in the "license" file accompanying this file. This file is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13 | * express or implied. See the License for the specific language governing
14 | * permissions and limitations under the License.
15 | */
16 |
17 | package com.amplifyframework.ui.authenticator.states
18 |
19 | import com.amplifyframework.auth.MFAType
20 | import com.amplifyframework.auth.cognito.challengeResponse
21 | import com.amplifyframework.ui.authenticator.SignInContinueWithMfaSetupSelectionState
22 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
23 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
24 | import com.amplifyframework.ui.authenticator.forms.FieldKey
25 |
26 | internal class SignInContinueWithMfaSetupSelectionStateImpl(
27 | override val allowedMfaTypes: Set,
28 | private val onSubmit: suspend (selection: String) -> Unit,
29 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
30 | ) : BaseStateImpl(), SignInContinueWithMfaSetupSelectionState {
31 |
32 | init {
33 | form.addFields {
34 | mfaSelection()
35 | }
36 | form.fields[FieldKey.MfaSelection]?.state?.content = allowedMfaTypes.first().challengeResponse
37 | }
38 |
39 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
40 |
41 | override suspend fun continueSignIn() = doSubmit {
42 | val selected = form.fields[FieldKey.MfaSelection]!!.state.content
43 | onSubmit(selected)
44 | }
45 |
46 | override val step = AuthenticatorStep.SignInContinueWithMfaSetupSelection
47 | }
48 |
--------------------------------------------------------------------------------
/testing/src/main/java/com/amplifyframework/ui/testing/ComposeRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.testing
17 |
18 | import androidx.compose.ui.test.hasTestTag
19 | import androidx.compose.ui.test.hasText
20 | import androidx.compose.ui.test.junit4.ComposeTestRule
21 | import androidx.compose.ui.test.onNodeWithTag
22 | import androidx.compose.ui.test.onNodeWithText
23 | import androidx.compose.ui.test.performClick
24 | import androidx.compose.ui.test.performScrollTo
25 | import androidx.compose.ui.test.performTextInput
26 |
27 | /**
28 | * Base class for a [compose testing robot](https://proandroiddev.com/jetpack-compose-with-robot-testing-pattern-ad6293335a20)
29 | * pattern. Contains common matchers that can be used in specific implementations.
30 | *
31 | * When using a robot implementation for UI testing the test class should specify _what_ is being asserted, and the
32 | * robot specifies _how_ it is asserted.
33 | */
34 | open class ComposeRobot(protected val composeTestRule: ComposeTestRule) {
35 | fun assertExists(text: String) = composeTestRule.onNodeWithText(text).assertExists()
36 |
37 | fun assertExists(tag: String, text: String) = composeTestRule.onNode(hasTestTag(tag) and hasText(text))
38 | .assertExists()
39 |
40 | fun clickOnTag(tag: String) = composeTestRule.onNodeWithTag(tag)
41 | .performScrollTo()
42 | .performClick()
43 |
44 | fun writeTo(tag: String, text: String) = composeTestRule.onNodeWithTag(tag).performTextInput(text)
45 | }
46 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/DeliveryDetails.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui
17 |
18 | import androidx.compose.material3.Text
19 | import androidx.compose.runtime.Composable
20 | import androidx.compose.ui.Modifier
21 | import com.amplifyframework.auth.AuthCodeDeliveryDetails
22 | import com.amplifyframework.ui.authenticator.R
23 | import com.amplifyframework.ui.authenticator.util.annotatedStringResource
24 |
25 | /**
26 | * Displays a message outlining the information in an [AuthCodeDeliveryDetails].
27 | * @param details The [AuthCodeDeliveryDetails]. A generic message is shown if the details are null.
28 | * @param modifier The composable Modifier
29 | */
30 | @Composable
31 | internal fun DeliveryDetails(
32 | details: AuthCodeDeliveryDetails?,
33 | modifier: Modifier = Modifier
34 | ) {
35 | val content = when (details?.deliveryMedium) {
36 | AuthCodeDeliveryDetails.DeliveryMedium.EMAIL -> annotatedStringResource(
37 | R.string.amplify_ui_authenticator_confirmation_code_sent,
38 | details.destination
39 | )
40 | AuthCodeDeliveryDetails.DeliveryMedium.SMS -> annotatedStringResource(
41 | R.string.amplify_ui_authenticator_confirmation_code_sent,
42 | details.destination
43 | )
44 | else -> annotatedStringResource(R.string.amplify_ui_authenticator_confirmation_code_sent_unknown)
45 | }
46 | Text(modifier = modifier, text = content)
47 | }
48 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/QrCode.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.remember
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.geometry.Offset
8 | import androidx.compose.ui.geometry.Size
9 | import androidx.compose.ui.graphics.Color
10 | import com.google.zxing.EncodeHintType
11 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
12 | import com.google.zxing.qrcode.encoder.Encoder
13 |
14 | @Composable
15 | internal fun QrCode(
16 | uri: String,
17 | modifier: Modifier = Modifier
18 | ) {
19 | val qrCode = rememberQrCode(data = uri)
20 | val borderCells = 1 // Border cells are needed in case the background is black - code must have a white border
21 | val xCells = qrCode.width + borderCells * 2 // cells in qr code + border on each side
22 | val yCells = qrCode.height + borderCells * 2
23 |
24 | Canvas(modifier = modifier) {
25 | val xScale = size.width / xCells.toFloat()
26 | val yScale = size.height / yCells.toFloat()
27 | val scaleFactor = minOf(xScale, yScale)
28 |
29 | drawRect(
30 | color = Color.White,
31 | size = size
32 | )
33 | for (x in 0 until qrCode.width) {
34 | for (y in 0 until qrCode.height) {
35 | val drawPixel = qrCode.get(x, y) == 1.toByte()
36 | if (drawPixel) {
37 | drawRect(
38 | color = Color.Black,
39 | topLeft = Offset((x + borderCells) * scaleFactor, (y + borderCells) * scaleFactor),
40 | size = Size(scaleFactor, scaleFactor)
41 | )
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
48 | @Composable
49 | internal fun rememberQrCode(data: String) = remember(data) {
50 | Encoder.encode(
51 | data,
52 | ErrorCorrectionLevel.L,
53 | mapOf(EncodeHintType.CHARACTER_SET to "UTF-8")
54 | ).matrix
55 | }
56 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorButton.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui
2 |
3 | import androidx.compose.foundation.layout.fillMaxWidth
4 | import androidx.compose.material3.Button
5 | import androidx.compose.material3.OutlinedButton
6 | import androidx.compose.material3.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.res.stringResource
10 | import com.amplifyframework.ui.authenticator.R
11 |
12 | internal enum class ButtonStyle {
13 | Primary,
14 | Secondary
15 | }
16 |
17 | /**
18 | * The button displayed in Authenticator.
19 | * @param onClick The click handler for the button
20 | * @param loading True to show the [loadingIndicator] content, false to show the button label.
21 | * @param modifier The [Modifier] for the composable.
22 | * @param label The label for the button
23 | * @param loadingIndicator The content to show when loading.
24 | */
25 | @Composable
26 | internal fun AuthenticatorButton(
27 | onClick: () -> Unit,
28 | loading: Boolean,
29 | modifier: Modifier = Modifier,
30 | label: String = stringResource(R.string.amplify_ui_authenticator_button_submit),
31 | loadingIndicator: @Composable () -> Unit = { LoadingIndicator() },
32 | enabled: Boolean = true,
33 | style: ButtonStyle = ButtonStyle.Primary
34 | ) {
35 | if (style == ButtonStyle.Primary) {
36 | Button(
37 | modifier = modifier.fillMaxWidth(),
38 | onClick = onClick,
39 | enabled = enabled && !loading
40 | ) {
41 | if (loading) {
42 | loadingIndicator()
43 | } else {
44 | Text(label)
45 | }
46 | }
47 | } else {
48 | OutlinedButton(
49 | modifier = modifier.fillMaxWidth(),
50 | onClick = onClick,
51 | enabled = enabled && !loading
52 | ) {
53 | if (loading) {
54 | loadingIndicator()
55 | } else {
56 | Text(label)
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/PasskeyPromptCheck.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.util
2 |
3 | import com.amplifyframework.ui.authenticator.AuthenticatorConfiguration
4 | import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
5 | import com.amplifyframework.ui.authenticator.data.PasskeyPrompt
6 | import com.amplifyframework.ui.authenticator.data.UserInfo
7 | import com.amplifyframework.ui.authenticator.enums.SignInSource
8 |
9 | // Utility class for checking whether a user should be shown a passkey prompt
10 | internal class PasskeyPromptCheck(private val authProvider: AuthProvider, private val osBuild: OsBuild = OsBuild()) {
11 | suspend fun shouldPromptForPasskey(userInfo: UserInfo, config: AuthenticatorConfiguration): Boolean {
12 | // Ensure that userHasPasskey is the last check so that the network request can be short-circuited by
13 | // the local-only checks.
14 | val authFlow = config.authenticationFlow
15 | return authFlow is AuthenticationFlow.UserChoice &&
16 | deviceSupportsPasskeyCreation() &&
17 | passkeyPromptsEnabled(userInfo, authFlow) &&
18 | !userHasPasskey()
19 | }
20 |
21 | // Passkey creation supported starting with API 28
22 | private fun deviceSupportsPasskeyCreation() = osBuild.sdkInt >= 28
23 |
24 | // Check whether passkey prompts are enabled by configuration
25 | private fun passkeyPromptsEnabled(userInfo: UserInfo, authFlow: AuthenticationFlow.UserChoice): Boolean =
26 | when (userInfo.signInSource) {
27 | SignInSource.SignIn -> authFlow.passkeyPrompts.afterSignIn == PasskeyPrompt.Always
28 | SignInSource.AutoSignIn -> authFlow.passkeyPrompts.afterSignUp == PasskeyPrompt.Always
29 | }
30 |
31 | // Check if the user already has a passkey registered
32 | private suspend fun userHasPasskey() = when (val result = authProvider.getPasskeys()) {
33 | is AmplifyResult.Error -> true // Assume user already has passkey on error so we don't incorrectly prompt them
34 | is AmplifyResult.Success -> result.data.isNotEmpty()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/BaseStateImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.states
17 |
18 | import androidx.compose.runtime.Stable
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.runtime.setValue
21 | import com.amplifyframework.ui.authenticator.forms.FieldValidatorScopeImpl
22 | import com.amplifyframework.ui.authenticator.forms.FormStateImpl
23 |
24 | /**
25 | * Base state for the interactive step states
26 | */
27 | @Stable
28 | internal abstract class BaseStateImpl {
29 | val form: FormStateImpl = FormStateImpl()
30 |
31 | private var busy: Boolean = false
32 |
33 | // Validates the form and marks the state as busy while invoking the
34 | // submission function
35 | protected suspend fun doSubmit(func: suspend () -> Unit) {
36 | if (!busy) {
37 | if (validate()) {
38 | busy = true
39 | form.enabled = false
40 | func()
41 | form.enabled = true
42 | busy = false
43 | }
44 | }
45 | }
46 |
47 | private fun validate(): Boolean {
48 | val validationScope = FieldValidatorScopeImpl(form.fields.mapValues { it.value.state.content })
49 | form.fields.values.forEach { field ->
50 | validationScope.content = field.state.content
51 | field.state.error = validationScope.run(field.validator)
52 | }
53 | return form.fields.values.all { it.state.error == null }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 |
25 | POM_GROUP=com.amplifyframework.ui
26 | POM_URL=https://github.com/aws-amplify/amplify-ui-android
27 | POM_SCM_URL=https://github.com/aws-amplify/amplify-ui-android
28 | POM_SCM_CONNECTION=scm:git:git://github.com/aws-amplify/amplify-ui-android.git
29 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/aws-amplify/amplify-ui-android.git
30 |
31 | POM_LICENSE_NAME=Apache 2.0
32 | POM_LICENSE_URL=http://aws.amazon.com/apache2.0
33 | POM_LICENSE_DIST=repo
34 |
35 | POM_DEVELOPER_ID=amazonwebservices
36 | POM_DEVELOPER_NAME=amazonwebservices
37 | POM_DEVELOPER_ORGANIZATION=Amazon Web Services
38 | POM_DEVELOPER_ORGANIZATION_URL=http://aws.amazon.com
39 |
40 | # Verify roborazzy screenshots when running tests
41 | roborazzi.test.verify=true
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInSelectAuthFactorRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.test.hasTestTag
19 | import androidx.compose.ui.test.hasText
20 | import androidx.compose.ui.test.junit4.ComposeTestRule
21 | import com.amplifyframework.ui.authenticator.data.AuthFactor
22 | import com.amplifyframework.ui.authenticator.forms.FieldKey
23 | import com.amplifyframework.ui.authenticator.ui.TestTags
24 | import com.amplifyframework.ui.authenticator.ui.testTag
25 | import com.amplifyframework.ui.testing.ComposeTest
26 |
27 | fun ComposeTest.signInSelectAuthFactor(func: SignInSelectAuthFactorRobot.() -> Unit) =
28 | SignInSelectAuthFactorRobot(composeTestRule).func()
29 |
30 | class SignInSelectAuthFactorRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
31 | fun hasUsername(expected: String) = composeTestRule.onNode(
32 | hasTestTag(FieldKey.Username.testTag) and hasText(expected)
33 | ).assertExists()
34 |
35 | fun hasPasswordButton() = composeTestRule.onNode(hasTestTag(TestTags.AuthFactorPassword)).assertExists()
36 | fun hasPasskeyButton() = composeTestRule.onNode(hasTestTag(TestTags.AuthFactorPasskey)).assertExists()
37 | fun hasEmailButton() = composeTestRule.onNode(hasTestTag(TestTags.AuthFactorEmail)).assertExists()
38 | fun hasSmsButton() = composeTestRule.onNode(hasTestTag(TestTags.AuthFactorSms)).assertExists()
39 |
40 | fun clickOnAuthFactor(factor: AuthFactor) = clickOnTag(factor.testTag)
41 | }
42 |
--------------------------------------------------------------------------------
/liveness/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("amplify.android.ui.component")
3 | id("org.jetbrains.kotlin.plugin.serialization")
4 | }
5 |
6 | android {
7 | namespace = "com.amplifyframework.ui.liveness"
8 | defaultConfig {
9 | externalNativeBuild {
10 | cmake {
11 | cppFlags += "-std=c++17"
12 | arguments += listOf("-DCMAKE_VERBOSE_MAKEFILE=ON", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
13 | }
14 | }
15 | buildConfigField(
16 | "String",
17 | "LIVENESS_VERSION_NAME",
18 | "\"${project.properties["VERSION_NAME"]}\""
19 | )
20 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
21 | }
22 |
23 | externalNativeBuild {
24 | cmake {
25 | // AGP doesn"t allow us to use project.buildDir (or subdirs) for CMake"s generated
26 | // build files (ninja build files, CMakeCache.txt, etc.). Use a staging directory that
27 | // lives alongside the project"s buildDir.
28 | buildStagingDirectory = file("${project.buildDir}/../buildNative")
29 | path = file("src/main/cpp/CMakeLists.txt")
30 | version = "3.22.1"
31 | }
32 | }
33 |
34 | buildFeatures {
35 | mlModelBinding = true
36 | }
37 |
38 | androidResources {
39 | noCompress += "tflite"
40 | }
41 |
42 | compileOptions {
43 | isCoreLibraryDesugaringEnabled = true
44 | }
45 | }
46 |
47 | dependencies {
48 | implementation(platform(libs.androidx.compose.bom))
49 | implementation(libs.androidx.compose.material.icons)
50 |
51 | api(libs.amplify.api)
52 | api(libs.amplify.predictions)
53 |
54 | implementation(libs.bundles.camera)
55 | implementation(libs.bundles.compose)
56 |
57 | implementation(libs.androidx.futures)
58 | implementation(libs.androidx.lifecycle)
59 |
60 | implementation(libs.kotlin.serialization.json)
61 |
62 | implementation(libs.litert)
63 | implementation(libs.litert.support)
64 |
65 | coreLibraryDesugaring(libs.android.desugar)
66 |
67 | testImplementation(projects.testing)
68 | }
69 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/RadioGroup.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.ui
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.fillMaxWidth
8 | import androidx.compose.foundation.lazy.items
9 | import androidx.compose.material3.RadioButton
10 | import androidx.compose.material3.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.getValue
13 | import androidx.compose.runtime.setValue
14 | import androidx.compose.ui.Alignment
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.platform.testTag
17 | import androidx.compose.ui.unit.dp
18 |
19 | @Composable
20 | internal fun RadioGroup(
21 | items: List,
22 | selected: T,
23 | onSelect: (T) -> Unit,
24 | modifier: Modifier = Modifier,
25 | label: (T) -> String = { it.toString() },
26 | enabled: Boolean = true
27 | ) {
28 | Column(modifier = modifier) {
29 | items.forEach { item ->
30 | RadioItem(
31 | modifier = Modifier.fillMaxWidth(),
32 | selected = selected == item,
33 | onSelect = onSelect,
34 | value = item,
35 | label = label(item),
36 | enabled = enabled
37 | )
38 | }
39 | }
40 | }
41 |
42 | @Composable
43 | internal fun RadioItem(
44 | selected: Boolean,
45 | onSelect: (T) -> Unit,
46 | value: T,
47 | label: String,
48 | modifier: Modifier = Modifier,
49 | enabled: Boolean = true
50 | ) {
51 | Row(
52 | modifier = modifier.clickable { onSelect(value) }.testTag(value.toString()),
53 | horizontalArrangement = Arrangement.spacedBy(8.dp),
54 | verticalAlignment = Alignment.CenterVertically
55 | ) {
56 | RadioButton(
57 | selected = selected,
58 | onClick = { onSelect(value) },
59 | enabled = enabled
60 | )
61 | Text(label)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/.github/workflows/issue_comment.yml:
--------------------------------------------------------------------------------
1 | name: Issue Comment
2 |
3 | on:
4 | issue_comment:
5 | types: [created]
6 |
7 | jobs:
8 | notify:
9 | runs-on: ubuntu-latest
10 | permissions: {}
11 | if: ${{ !github.event.issue.pull_request }}
12 | steps:
13 | - name: Run webhook curl command
14 | env:
15 | WEBHOOK_URL: ${{ secrets.SLACK_COMMENT_WEBHOOK_URL }}
16 | BODY: ${{ toJson(github.event.comment.body) }}
17 | COMMENT_URL: ${{ github.event.comment.html_url }}
18 | USER: ${{ github.event.comment.user.login }}
19 | shell: bash
20 | run: echo $BODY | sed "s/\\\n/. /g; s/\\\r//g; s/[^a-zA-Z0-9 &().,:]//g" | xargs -I {} curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data '{"body":"{}", "issue":"'$COMMENT_URL'", "user":"'$USER'"}'
21 |
22 | adjust-labels:
23 | runs-on: ubuntu-latest
24 | permissions:
25 | issues: write
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 | ISSUE_NUMBER: ${{ github.event.issue.number }}
29 | REPOSITORY_NAME: ${{ github.event.repository.full_name }}
30 | steps:
31 | - name: remove pending-community-response when new comment received
32 | if: ${{ !contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) && !github.event.issue.pull_request }}
33 | shell: bash
34 | run: |
35 | gh issue edit $ISSUE_NUMBER --repo $REPOSITORY_NAME --remove-label "pending-community-response"
36 | - name: add pending-maintainer-response when new community comment received
37 | if: ${{ !contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) }}
38 | shell: bash
39 | run: |
40 | gh issue edit $ISSUE_NUMBER --repo $REPOSITORY_NAME --add-label "pending-maintainer-response"
41 | - name: remove pending-maintainer-response when new owner/member comment received
42 | if: ${{ contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) }}
43 | shell: bash
44 | run: |
45 | gh issue edit $ISSUE_NUMBER --repo $REPOSITORY_NAME --remove-label "pending-maintainer-response"
46 |
47 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/ScreenLevelRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.autofill.ContentType
19 | import androidx.compose.ui.semantics.SemanticsProperties
20 | import androidx.compose.ui.test.SemanticsMatcher
21 | import androidx.compose.ui.test.hasAnyAncestor
22 | import androidx.compose.ui.test.hasTestTag
23 | import androidx.compose.ui.test.junit4.ComposeTestRule
24 | import androidx.compose.ui.test.performClick
25 | import com.amplifyframework.ui.authenticator.forms.FieldKey
26 | import com.amplifyframework.ui.authenticator.ui.TestTags
27 | import com.amplifyframework.ui.authenticator.ui.testTag
28 | import com.amplifyframework.ui.testing.ComposeRobot
29 |
30 | abstract class ScreenLevelRobot(rule: ComposeTestRule) : ComposeRobot(rule) {
31 | // Check that the composable has the expected title
32 | fun hasTitle(expected: String) = assertExists(TestTags.AuthenticatorTitle, expected)
33 |
34 | // Check that the expected content type is set
35 | fun hasContentType(key: FieldKey, contentType: ContentType) = composeTestRule.onNode(
36 | hasTestTag(key.testTag) and SemanticsMatcher.expectValue(SemanticsProperties.ContentType, contentType)
37 | ).assertExists()
38 |
39 | fun setFieldContent(key: FieldKey, content: String) = writeTo(key.testTag, content)
40 |
41 | fun clickOnShowIcon(key: FieldKey) = composeTestRule.onNode(
42 | hasTestTag(TestTags.ShowPasswordIcon) and hasAnyAncestor(hasTestTag(key.testTag))
43 | ).performClick()
44 | }
45 |
--------------------------------------------------------------------------------
/samples/liveness/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
19 |
20 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/testing/src/main/java/com/amplifyframework/ui/testing/ComposeTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.testing
17 |
18 | import androidx.compose.foundation.layout.Box
19 | import androidx.compose.foundation.layout.fillMaxSize
20 | import androidx.compose.foundation.rememberScrollState
21 | import androidx.compose.foundation.verticalScroll
22 | import androidx.compose.runtime.Composable
23 | import androidx.compose.ui.Modifier
24 | import androidx.compose.ui.test.junit4.createComposeRule
25 | import androidx.test.ext.junit.runners.AndroidJUnit4
26 | import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
27 | import org.junit.Before
28 | import org.junit.Rule
29 | import org.junit.runner.RunWith
30 | import org.robolectric.annotation.Config
31 | import org.robolectric.annotation.GraphicsMode
32 | import org.robolectric.shadows.ShadowLog
33 |
34 | @RunWith(AndroidJUnit4::class)
35 | @GraphicsMode(GraphicsMode.Mode.NATIVE)
36 | @Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel6)
37 | abstract class ComposeTest {
38 | @get:Rule
39 | val composeTestRule = createComposeRule()
40 |
41 | @get:Rule
42 | val screenshotRule = ScreenshotRule(composeTestRule)
43 |
44 | @Before
45 | @Throws(Exception::class)
46 | fun setupLogging() {
47 | ShadowLog.stream = System.out // Redirect Logcat to console
48 | }
49 |
50 | protected open fun setContent(content: @Composable () -> Unit) = composeTestRule.setContent {
51 | Box(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
52 | content()
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorForm.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui
17 |
18 | import androidx.compose.foundation.layout.Arrangement
19 | import androidx.compose.foundation.layout.Column
20 | import androidx.compose.foundation.layout.Spacer
21 | import androidx.compose.foundation.layout.fillMaxWidth
22 | import androidx.compose.foundation.layout.size
23 | import androidx.compose.runtime.Composable
24 | import androidx.compose.ui.Alignment
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.platform.testTag
27 | import androidx.compose.ui.unit.dp
28 | import com.amplifyframework.ui.authenticator.forms.MutableFormState
29 |
30 | /**
31 | * Displays the input fields for a [MutableFormState].
32 | * @param state The [MutableFormState] holding the form's state.
33 | * @param modifier The Modifier for the composable.
34 | */
35 | @Composable
36 | internal fun AuthenticatorForm(state: MutableFormState, modifier: Modifier = Modifier) {
37 | Column(
38 | modifier = modifier,
39 | horizontalAlignment = Alignment.CenterHorizontally,
40 | verticalArrangement = Arrangement.spacedBy(8.dp)
41 | ) {
42 | state.fields.values.forEach { field ->
43 | AuthenticatorField(
44 | modifier = Modifier.fillMaxWidth().testTag(field.config.key.testTag),
45 | fieldConfig = field.config,
46 | fieldState = field.state,
47 | formState = state
48 | )
49 | }
50 | Spacer(modifier = Modifier.size(16.dp))
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignUpRobot.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.ui.robots
17 |
18 | import androidx.compose.ui.autofill.ContentType
19 | import androidx.compose.ui.test.junit4.ComposeTestRule
20 | import com.amplifyframework.ui.authenticator.forms.FieldKey
21 | import com.amplifyframework.ui.authenticator.ui.TestTags
22 | import com.amplifyframework.ui.testing.ComposeTest
23 |
24 | fun ComposeTest.signUp(func: SignUpRobot.() -> Unit) = SignUpRobot(composeTestRule).func()
25 |
26 | class SignUpRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
27 | fun hasSubmitButton(expected: String) = assertExists(TestTags.SignUpButton, expected)
28 | fun hasUsernameContentType(contentType: ContentType) = hasContentType(FieldKey.Username, contentType)
29 | fun hasPasswordContentType(contentType: ContentType) = hasContentType(FieldKey.Password, contentType)
30 | fun hasConfirmPasswordContentType(contentType: ContentType) = hasContentType(FieldKey.ConfirmPassword, contentType)
31 |
32 | fun setUsername(value: String) = setFieldContent(FieldKey.Username, value)
33 | fun setPassword(value: String) = setFieldContent(FieldKey.Password, value)
34 | fun setConfirmPassword(value: String) = setFieldContent(FieldKey.ConfirmPassword, value)
35 | fun setEmail(value: String) = setFieldContent(FieldKey.Email, value)
36 | fun clickShowPassword(fieldKey: FieldKey) = clickOnShowIcon(fieldKey)
37 | fun clickSubmitButton() = clickOnTag(TestTags.SignUpButton)
38 | fun clickBackToSignIn() = clickOnTag(TestTags.BackToSignInButton)
39 | }
40 |
--------------------------------------------------------------------------------
/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInSelectAuthFactorStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.amplifyframework.ui.authenticator.states
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.amplifyframework.ui.authenticator.SignInSelectAuthFactorState
7 | import com.amplifyframework.ui.authenticator.SignInSelectAuthFactorState.Action
8 | import com.amplifyframework.ui.authenticator.auth.SignInMethod
9 | import com.amplifyframework.ui.authenticator.data.AuthFactor
10 | import com.amplifyframework.ui.authenticator.data.containsPassword
11 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
12 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
13 |
14 | internal class SignInSelectAuthFactorStateImpl(
15 | override val username: String,
16 | val signInMethod: SignInMethod,
17 | override val availableAuthFactors: Set,
18 | private val onSubmit: suspend (authFactor: AuthFactor) -> Unit,
19 | private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
20 | ) : BaseStateImpl(),
21 | SignInSelectAuthFactorState,
22 | MutableActionState {
23 | override val step: AuthenticatorStep = AuthenticatorStep.SignInSelectAuthFactor
24 |
25 | override var action: Action? by mutableStateOf(null)
26 |
27 | init {
28 | if (availableAuthFactors.containsPassword()) {
29 | form.addFields { password() }
30 | }
31 | }
32 |
33 | override fun moveTo(step: AuthenticatorInitialStep) = onMoveTo(step)
34 |
35 | override suspend fun select(authFactor: AuthFactor) = withAction(Action.SelectAuthFactor(authFactor)) {
36 | // Clear errors
37 | form.fields.values.forEach { it.state.error = null }
38 |
39 | form.enabled = false
40 | onSubmit(authFactor)
41 | form.enabled = true
42 | }
43 | }
44 |
45 | internal fun SignInSelectAuthFactorState.getPasswordFactor(): AuthFactor =
46 | availableAuthFactors.first { it is AuthFactor.Password }
47 |
48 | internal val SignInSelectAuthFactorState.signInMethod: SignInMethod
49 | get() = (this as SignInSelectAuthFactorStateImpl).signInMethod
50 |
--------------------------------------------------------------------------------
/.github/workflows/release_pr.yml:
--------------------------------------------------------------------------------
1 | name: Prepare Next Release
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | component:
6 | type: choice
7 | description: Component to release
8 | required: true
9 | options:
10 | - Authenticator
11 | - Liveness
12 | release_tag:
13 | description: 'Previous Release Tag'
14 | required: false
15 | type: string
16 | env:
17 | GIT_USER_NAME: amplify-android-dev+ghops
18 | GIT_USER_EMAIL: amplify-android-dev+ghops@amazon.com
19 | BASE_BRANCH: ${{ github.ref_name }}
20 | jobs:
21 | create_pr_for_next_release:
22 | runs-on: ubuntu-latest
23 | steps:
24 | - name: Update git
25 | run: |
26 | sudo add-apt-repository -y ppa:git-core/ppa
27 | sudo apt-get update
28 | sudo apt-get install git -y
29 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
30 | with:
31 | ref: ${{ env.BASE_BRANCH }}
32 | fetch-depth: 0
33 | - name: Set up Ruby
34 | uses: ruby/setup-ruby@32110d4e311bd8996b2a82bf2a43b714ccc91777 # v1.221.0
35 | with:
36 | ruby-version: "3.0"
37 | - name: Install dependencies
38 | run: |
39 | cd scripts
40 | gem install bundler
41 | bundle install --full-index
42 | - name: Configure git options
43 | run: |
44 | cd scripts
45 | bundle exec fastlane android configure_git_options git_user_email:$GIT_USER_EMAIL git_user_name:$GIT_USER_NAME
46 | - name: Create/checkout a branch for the release
47 | run: |
48 | branch_name=bump_version_${{ env.BASE_BRANCH }}
49 | git fetch --all
50 | (git branch -D $branch_name &>/dev/null) && (echo 'Existing $branch_name branch deleted') || (echo 'No existing $branch_name branch to delete.')
51 | git checkout -b $branch_name
52 | - name: Create PR for next release
53 | env:
54 | RELEASE_MANAGER_TOKEN: ${{secrets.GITHUB_TOKEN}}
55 | RELEASE_TAG: ${{ github.event.inputs.release_tag }}
56 | COMPONENT: ${{ github.event.inputs.component }}
57 | run: |
58 | cd scripts
59 | bundle exec fastlane android create_next_release_pr release_tag:"$RELEASE_TAG" base_branch:"$BASE_BRANCH" release_component_target:"$COMPONENT"
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Built application files
4 | *.apk
5 | *.ap_
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 |
18 | # Gradle files
19 | .gradle/
20 | build/
21 | .gradle/**
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # Log Files
30 | *.log
31 |
32 | # Android Studio Navigation editor temp files
33 | .navigation/
34 |
35 | # Android Studio captures folder
36 | captures/
37 |
38 | # IntelliJ
39 | *.iml
40 | **/*.iml
41 | **/.idea/workspace.xml
42 | **/.idea/tasks.xml
43 | **/.idea/gradle.xml
44 | **/.idea/assetWizardSettings.xml
45 | **/.idea/dictionaries
46 | **/.idea/libraries
47 | **/.idea/caches
48 | **/.idea/misc.xml
49 | **/.idea/**
50 |
51 | # VIM
52 | *.swp
53 | *.swo
54 |
55 | # Keystore files
56 | # Uncomment the following line if you do not want to check your keystore files in.
57 | *.jks
58 |
59 | # External native build folder generated in Android Studio 2.2 and later
60 | .externalNativeBuild
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | scripts/fastlane/report.xml
72 | scripts/fastlane/Preview.html
73 | scripts/fastlane/screenshots
74 | scripts/fastlane/test_output
75 | scripts/fastlane/readme.md
76 |
77 | # maven file
78 | target/
79 |
80 | #python comile
81 | __pycache__/
82 |
83 | # Credentials
84 | **/awsconfiguration.json
85 | **/amplifyconfiguration.json
86 | **/amplifyconfiguration_v2.json
87 | **/credentials.json
88 | **/google_client_creds.json
89 |
90 | # IDE files
91 | .idea/**
92 |
93 | # Gradle
94 | .gradle/**
95 |
96 | #amplify
97 | amplify/
98 | build/
99 | dist/
100 | node_modules/
101 | aws-exports.js
102 | awsconfiguration.json
103 | amplifyconfiguration.json
104 |
105 | test-results/
106 | build-artifacts/
107 | build-artifacts.tar.gz
108 |
109 | # add ignore for jenv
110 | .java-version
111 |
112 | # sample app resources
113 | .graphqlconfig.yml
114 | aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json
115 |
--------------------------------------------------------------------------------
/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/AuthenticatorUiTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | package com.amplifyframework.ui.authenticator.testUtil
17 |
18 | import androidx.compose.foundation.layout.Box
19 | import androidx.compose.foundation.layout.padding
20 | import androidx.compose.runtime.Composable
21 | import androidx.compose.runtime.CompositionLocalProvider
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.autofill.AutofillManager
24 | import androidx.compose.ui.platform.LocalAutofillManager
25 | import androidx.compose.ui.unit.dp
26 | import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
27 | import com.amplifyframework.ui.authenticator.locals.LocalAuthenticatorStep
28 | import com.amplifyframework.ui.authenticator.theme.AmplifyTheme
29 | import com.amplifyframework.ui.testing.ComposeTest
30 |
31 | abstract class AuthenticatorUiTest : ComposeTest() {
32 | fun setContent(
33 | providedStep: AuthenticatorStep? = null,
34 | autofillManager: AutofillManager? = null,
35 | content: @Composable () -> Unit
36 | ) = setContent {
37 | val step = providedStep ?: AuthenticatorStep.Loading
38 | CompositionLocalProvider(
39 | LocalAutofillManager provides autofillManager,
40 | LocalAuthenticatorStep provides step
41 | ) {
42 | content()
43 | }
44 | }
45 |
46 | override fun setContent(content: @Composable () -> Unit) {
47 | super.setContent {
48 | AmplifyTheme {
49 | Box(modifier = Modifier.padding(top = 16.dp)) {
50 | content()
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------