From b1fd18d23ea0ea30bf54a8396fccbbae2430f792 Mon Sep 17 00:00:00 2001 From: AWS Date: Thu, 31 Aug 2017 21:45:14 +0000 Subject: [PATCH] AWS SDK for Android 2.6.0 --- CHANGELOG.md | 31 + aws-android-sdk-apigateway-core/pom.xml | 4 +- aws-android-sdk-auth-core/pom.xml | 84 ++ .../src/main/AndroidManifest.xml | 9 + .../auth/core/DefaultSignInResultHandler.java | 58 ++ .../mobile/auth/core/IdentityHandler.java | 38 + .../mobile/auth/core/IdentityManager.java | 832 ++++++++++++++++++ .../mobile/auth/core/IdentityProvider.java | 72 ++ .../mobile/auth/core/SignInResultHandler.java | 55 ++ .../auth/core/SignInStateChangeListener.java | 35 + .../auth/core/StartupAuthErrorDetails.java | 62 ++ .../mobile/auth/core/StartupAuthResult.java | 67 ++ .../auth/core/StartupAuthResultHandler.java | 34 + .../auth/core/internal/util/ThreadUtils.java | 40 + .../auth/core/internal/util/ViewHelper.java | 78 ++ .../auth/core/internal/util/package-info.java | 18 + .../mobile/auth/core/package-info.java | 18 + .../auth/core/signin/AuthException.java | 53 ++ .../core/signin/CognitoAuthException.java | 35 + .../core/signin/ProviderAuthException.java | 35 + .../auth/core/signin/SignInManager.java | 308 +++++++ .../core/signin/SignInPermissionsHandler.java | 38 + .../auth/core/signin/SignInProvider.java | 58 ++ .../signin/SignInProviderResultHandler.java | 48 + .../auth/core/signin/ui/DisplayUtils.java | 63 ++ .../signin/ui/SplitBackgroundDrawable.java | 79 ++ .../core/signin/ui/buttons/SignInButton.java | 316 +++++++ .../ui/buttons/SignInButtonAttributes.java | 124 +++ .../src/main/res/values/attrs.xml | 10 + .../src/main/res/values/strings.xml | 5 + aws-android-sdk-auth-facebook/pom.xml | 99 +++ .../src/main/AndroidManifest.xml | 13 + .../mobile/auth/facebook/FacebookButton.java | 103 +++ .../auth/facebook/FacebookSignInProvider.java | 292 ++++++ .../main/res/drawable-hdpi/facebook_icon.png | Bin 0 -> 1054 bytes .../main/res/drawable-ldpi/facebook_icon.png | Bin 0 -> 699 bytes .../main/res/drawable-mdpi/facebook_icon.png | Bin 0 -> 919 bytes .../main/res/drawable-tvdpi/facebook_icon.png | Bin 0 -> 901 bytes .../main/res/drawable-xhdpi/facebook_icon.png | Bin 0 -> 1414 bytes .../res/drawable-xxhdpi/facebook_icon.png | Bin 0 -> 2008 bytes .../res/drawable-xxxhdpi/facebook_icon.png | Bin 0 -> 2484 bytes .../src/main/res/values/strings.xml | 3 + aws-android-sdk-auth-google/pom.xml | 97 ++ .../src/main/AndroidManifest.xml | 15 + .../mobile/auth/google/GoogleButton.java | 113 +++ .../auth/google/GoogleSignInException.java | 51 ++ .../auth/google/GoogleSignInProvider.java | 479 ++++++++++ .../main/res/drawable-hdpi/google_icon.png | Bin 0 -> 6056 bytes .../main/res/drawable-ldpi/google_icon.png | Bin 0 -> 2877 bytes .../main/res/drawable-mdpi/google_icon.png | Bin 0 -> 4223 bytes .../main/res/drawable-tvdpi/google_icon.png | Bin 0 -> 5123 bytes .../main/res/drawable-xhdpi/google_icon.png | Bin 0 -> 5516 bytes .../main/res/drawable-xxhdpi/google_icon.png | Bin 0 -> 18170 bytes .../main/res/drawable-xxxhdpi/google_icon.png | Bin 0 -> 29568 bytes .../src/main/res/values/strings.xml | 3 + aws-android-sdk-auth-ui/pom.xml | 114 +++ .../src/main/AndroidManifest.xml | 20 + .../mobile/auth/ui/AuthUIConfiguration.java | 204 +++++ .../mobile/auth/ui/SignInActivity.java | 190 ++++ .../amazonaws/mobile/auth/ui/SignInView.java | 533 +++++++++++ .../mobile/auth/ui/package-info.java | 21 + .../drawable-hdpi/default_sign_in_logo.png | Bin 0 -> 21897 bytes .../drawable-ldpi/default_sign_in_logo.png | Bin 0 -> 10385 bytes .../drawable-mdpi/default_sign_in_logo.png | Bin 0 -> 15374 bytes .../drawable-tvdpi/default_sign_in_logo.png | Bin 0 -> 16272 bytes .../drawable-xhdpi/default_sign_in_logo.png | Bin 0 -> 14736 bytes .../drawable-xxhdpi/default_sign_in_logo.png | Bin 0 -> 65734 bytes .../drawable-xxxhdpi/default_sign_in_logo.png | Bin 0 -> 103772 bytes .../src/main/res/layout/activity_sign_in.xml | 9 + .../layout/horizontal_or_sign_in_divider.xml | 36 + .../res/layout/horizontal_sign_in_divider.xml | 36 + .../src/main/res/values/attrs.xml | 22 + .../src/main/res/values/colors.xml | 4 + .../src/main/res/values/dimens.xml | 7 + .../src/main/res/values/strings.xml | 8 + aws-android-sdk-auth-userpools/pom.xml | 90 ++ .../src/main/AndroidManifest.xml | 43 + .../CognitoUserPoolsSignInProvider.java | 750 ++++++++++++++++ .../userpools/ForgotPasswordActivity.java | 60 ++ .../auth/userpools/ForgotPasswordView.java | 159 ++++ .../mobile/auth/userpools/FormEditText.java | 184 ++++ .../mobile/auth/userpools/FormView.java | 159 ++++ .../mobile/auth/userpools/MFAActivity.java | 59 ++ .../mobile/auth/userpools/MFAView.java | 145 +++ .../mobile/auth/userpools/SignUpActivity.java | 79 ++ .../auth/userpools/SignUpConfirmActivity.java | 71 ++ .../auth/userpools/SignUpConfirmView.java | 154 ++++ .../mobile/auth/userpools/SignUpView.java | 198 +++++ .../auth/userpools/UserPoolFormConstants.java | 37 + .../auth/userpools/UserPoolSignInView.java | 238 +++++ .../mobile/auth/userpools/package-info.java | 21 + .../res/layout/activity_forgot_password.xml | 55 ++ .../src/main/res/layout/activity_mfa.xml | 55 ++ .../src/main/res/layout/activity_sign_up.xml | 50 ++ .../res/layout/activity_sign_up_confirm.xml | 65 ++ .../src/main/res/values/attrs.xml | 15 + .../src/main/res/values/dimens.xml | 10 + .../src/main/res/values/ids.xml | 4 + .../src/main/res/values/strings.xml | 48 + aws-android-sdk-autoscaling/pom.xml | 4 +- aws-android-sdk-cloudwatch/pom.xml | 4 +- aws-android-sdk-cognito/pom.xml | 6 +- .../cognito/CognitoSyncManager.java | 37 + ...azonaws.aws-android-sdk-cognito.properties | 2 +- aws-android-sdk-cognitoauth/pom.xml | 7 +- .../pom.xml | 6 +- .../CognitoUserPool.java | 28 + aws-android-sdk-core/pom.xml | 2 +- .../CognitoCachingCredentialsProvider.java | 43 + .../auth/CognitoCredentialsProvider.java | 57 ++ .../mobile/config/AWSConfiguration.java | 179 ++++ .../java/com/amazonaws/regions/Region.java | 9 + .../com/amazonaws/regions/RegionDefaults.java | 332 +++---- .../com/amazonaws/util/VersionInfoUtils.java | 2 +- ....amazonaws.aws-android-sdk-core.properties | 2 +- .../amazonaws/util/VersionInfoUtilsTest.java | 2 +- aws-android-sdk-ddb-document/pom.xml | 6 +- aws-android-sdk-ddb-mapper/pom.xml | 8 +- .../dynamodbmapper/DynamoDBMapper.java | 191 +++- aws-android-sdk-ddb/pom.xml | 4 +- aws-android-sdk-ec2/pom.xml | 4 +- aws-android-sdk-elb/pom.xml | 4 +- aws-android-sdk-iot/pom.xml | 4 +- aws-android-sdk-kinesis/pom.xml | 4 +- aws-android-sdk-kms/pom.xml | 4 +- aws-android-sdk-lambda/pom.xml | 4 +- .../lambdainvoker/LambdaInvokerFactory.java | 209 +++++ aws-android-sdk-lex/pom.xml | 7 +- .../arm64-v8a/libblueshift-audioprocessing.so | Bin 0 -> 243080 bytes .../libblueshift-audioprocessing.so | Bin 279676 -> 238740 bytes .../armeabi/libblueshift-audioprocessing.so | Bin 287864 -> 246924 bytes .../jni/mips/libblueshift-audioprocessing.so | Bin 0 -> 269480 bytes .../mips64/libblueshift-audioprocessing.so | Bin 0 -> 245808 bytes .../jni/x86/libblueshift-audioprocessing.so | Bin 300156 -> 242732 bytes .../x86_64/libblueshift-audioprocessing.so | Bin 0 -> 247368 bytes aws-android-sdk-logs/pom.xml | 4 +- aws-android-sdk-machinelearning/pom.xml | 4 +- aws-android-sdk-mobileanalytics/pom.xml | 4 +- aws-android-sdk-pinpoint/pom.xml | 4 +- .../pinpoint/PinpointConfiguration.java | 61 +- .../endpointProfile/EndpointProfile.java | 22 +- .../targeting/notification/AppUtil.java | 37 + .../notification/NotificationClient.java | 33 +- .../targeting/EndpointProfileTest.java | 17 + .../notification/NotificationClientTest.java | 236 ++++- aws-android-sdk-polly/pom.xml | 4 +- aws-android-sdk-rekognition/pom.xml | 4 +- aws-android-sdk-s3/pom.xml | 6 +- .../s3/transferutility/TransferUtility.java | 265 +++++- aws-android-sdk-sdb/pom.xml | 4 +- aws-android-sdk-ses/pom.xml | 4 +- aws-android-sdk-sns/pom.xml | 6 +- aws-android-sdk-sqs/pom.xml | 4 +- pom.xml | 7 +- 154 files changed, 9504 insertions(+), 321 deletions(-) create mode 100644 aws-android-sdk-auth-core/pom.xml create mode 100644 aws-android-sdk-auth-core/src/main/AndroidManifest.xml create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/DefaultSignInResultHandler.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityHandler.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityManager.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityProvider.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInResultHandler.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInStateChangeListener.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthErrorDetails.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResult.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResultHandler.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ThreadUtils.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ViewHelper.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/package-info.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/package-info.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/AuthException.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/CognitoAuthException.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ProviderAuthException.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInManager.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInPermissionsHandler.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProvider.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProviderResultHandler.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/DisplayUtils.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/SplitBackgroundDrawable.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButton.java create mode 100644 aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButtonAttributes.java create mode 100644 aws-android-sdk-auth-core/src/main/res/values/attrs.xml create mode 100644 aws-android-sdk-auth-core/src/main/res/values/strings.xml create mode 100644 aws-android-sdk-auth-facebook/pom.xml create mode 100644 aws-android-sdk-auth-facebook/src/main/AndroidManifest.xml create mode 100644 aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookButton.java create mode 100644 aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookSignInProvider.java create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-hdpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-ldpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-mdpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-tvdpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-xhdpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-xxhdpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/drawable-xxxhdpi/facebook_icon.png create mode 100644 aws-android-sdk-auth-facebook/src/main/res/values/strings.xml create mode 100644 aws-android-sdk-auth-google/pom.xml create mode 100644 aws-android-sdk-auth-google/src/main/AndroidManifest.xml create mode 100644 aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleButton.java create mode 100644 aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInException.java create mode 100644 aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInProvider.java create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-hdpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-ldpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-mdpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-tvdpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-xhdpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-xxhdpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/drawable-xxxhdpi/google_icon.png create mode 100644 aws-android-sdk-auth-google/src/main/res/values/strings.xml create mode 100644 aws-android-sdk-auth-ui/pom.xml create mode 100644 aws-android-sdk-auth-ui/src/main/AndroidManifest.xml create mode 100644 aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/AuthUIConfiguration.java create mode 100644 aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInActivity.java create mode 100644 aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInView.java create mode 100644 aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/package-info.java create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-hdpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-ldpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-mdpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-tvdpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-xhdpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-xxhdpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/drawable-xxxhdpi/default_sign_in_logo.png create mode 100644 aws-android-sdk-auth-ui/src/main/res/layout/activity_sign_in.xml create mode 100644 aws-android-sdk-auth-ui/src/main/res/layout/horizontal_or_sign_in_divider.xml create mode 100644 aws-android-sdk-auth-ui/src/main/res/layout/horizontal_sign_in_divider.xml create mode 100644 aws-android-sdk-auth-ui/src/main/res/values/attrs.xml create mode 100644 aws-android-sdk-auth-ui/src/main/res/values/colors.xml create mode 100644 aws-android-sdk-auth-ui/src/main/res/values/dimens.xml create mode 100644 aws-android-sdk-auth-ui/src/main/res/values/strings.xml create mode 100644 aws-android-sdk-auth-userpools/pom.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/AndroidManifest.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/CognitoUserPoolsSignInProvider.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordActivity.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordView.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormEditText.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormView.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAActivity.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAView.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpActivity.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmActivity.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmView.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpView.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolFormConstants.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolSignInView.java create mode 100644 aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/package-info.java create mode 100644 aws-android-sdk-auth-userpools/src/main/res/layout/activity_forgot_password.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/layout/activity_mfa.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/layout/activity_sign_up.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/layout/activity_sign_up_confirm.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/values/attrs.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/values/dimens.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/values/ids.xml create mode 100644 aws-android-sdk-auth-userpools/src/main/res/values/strings.xml create mode 100644 aws-android-sdk-core/src/main/java/com/amazonaws/mobile/config/AWSConfiguration.java create mode 100644 aws-android-sdk-lex/src/main/jni/arm64-v8a/libblueshift-audioprocessing.so create mode 100644 aws-android-sdk-lex/src/main/jni/mips/libblueshift-audioprocessing.so create mode 100644 aws-android-sdk-lex/src/main/jni/mips64/libblueshift-audioprocessing.so create mode 100644 aws-android-sdk-lex/src/main/jni/x86_64/libblueshift-audioprocessing.so create mode 100644 aws-android-sdk-pinpoint/src/main/java/com/amazonaws/mobileconnectors/pinpoint/targeting/notification/AppUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c45f5450308..4454e083909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # Change Log - AWS SDK for Android +## [Release 2.6.0](https://github.com/aws/aws-sdk-android/releases/tag/release_v2.6.0) + +### New Features: + +- **AWS Auth SDK** + - Added new SDK for configurable User SignIn Screen with Amazon Cognito UserPools, Facebook SignIn and Google SignIn. + +- **AWS Core Runtime** + - Added support for a configuration file `awsconfiguration.json` that can be used to construct: + - `CognitoCredentialsProvider`, `CognitoCachingCredentialsProvider`, `CognitoUserPool`, `TransferUtility`, `DynamoDBMapper`, `PinpointConfiguration`, `CognitoSyncManager`, and `LambdaInvokerFactory`. + +### Improvements: + +- **AWS S3** + - Add builder pattern constructor to `TransferUtility`. + - Add default bucket property in `TransferUtility` builder. The default bucket will be used when no bucket is specified. + +- **AWS Lambda** + - Add builder pattern constructor to `LambdaInvokerFactory`. + +- **Amazon DynamoDB** + - Add builder pattern constructor to `DynamoDBMapper`. + +- **Amazon Pinpoint** + - Add configuration option to post notifications even if the app is in the foreground. + +### Bug Fixes: + +- **Amazon Pinpoint** + - Fixed bug that caused Pinpoint endpoint profile to incorrectly calculate the number of profile attributes and metrics. + ## [Release 2.4.7](https://github.com/aws/aws-sdk-android/releases/tag/release_v2.4.7) ### Improvements: diff --git a/aws-android-sdk-apigateway-core/pom.xml b/aws-android-sdk-apigateway-core/pom.xml index 50a68ff70b0..6331ad326e2 100644 --- a/aws-android-sdk-apigateway-core/pom.xml +++ b/aws-android-sdk-apigateway-core/pom.xml @@ -12,7 +12,7 @@ com.amazonaws aws-android-sdk-pom - 2.4.7 + 2.6.0 @@ -20,7 +20,7 @@ com.amazonaws aws-android-sdk-core false - 2.4.7 + 2.6.0 diff --git a/aws-android-sdk-auth-core/pom.xml b/aws-android-sdk-auth-core/pom.xml new file mode 100644 index 00000000000..0a88ad4ef2b --- /dev/null +++ b/aws-android-sdk-auth-core/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + com.amazonaws + aws-android-sdk-auth-core + aar + AWS SDK for Android - AWS Authentication Core + The AWS Android SDK for AWS Authentication Core module holds the client classes that are used for enabling communication with Amazon CognitoIdentityProvider, Amazon Cognito UserPools, Facebook and Google SignIn Providers + http://aws.amazon.com/sdkforandroid + + + + UTF-8 + + + UTF-8 + + + + + com.amazonaws + aws-android-sdk-pom + 2.6.0 + + + + + android-support + file://${env.ANDROID_HOME}/extras/android/m2repository/ + + + + + + com.amazonaws + aws-android-sdk-core + false + 2.6.0 + + + + com.google.android + android + 4.1.1.4 + provided + + + + com.android.support + support-v4 + 24.2.0 + aar + provided + + + + + + + com.simpligility.maven.plugins + android-maven-plugin + 4.5.0 + true + + + 11 + 19.1.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + diff --git a/aws-android-sdk-auth-core/src/main/AndroidManifest.xml b/aws-android-sdk-auth-core/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..d0d3b575f78 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/DefaultSignInResultHandler.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/DefaultSignInResultHandler.java new file mode 100644 index 00000000000..eb4a3166c6c --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/DefaultSignInResultHandler.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.util.Log; +import android.widget.Toast; + +import com.amazonaws.mobile.auth.core.R; + +/** + * A default base class easing the work required for implementing the SignInResultHandler for + * {@link IdentityManager#signInOrSignUp(Context, SignInResultHandler)} by providing default + * behavior in the case that the user cancels signing in or encounters an error. The default for + * canceling is to toast that sign-in was canceled. The default for a sign-in error is to show + * an alert dialog specifying the error message. + */ +public abstract class DefaultSignInResultHandler implements SignInResultHandler { + private static final String LOG_TAG = DefaultSignInResultHandler.class.getSimpleName(); + + /** + * User cancelled signing in with a provider on the sign-in activity. + * Note: The user is still on the sign-in activity when this call is made. + * @param provider the provider the user canceled with. + */ + public void onIntermediateProviderCancel(Activity callingActivity, IdentityProvider provider) { + Log.d(LOG_TAG, String.format("%s Sign-In flow is canceled", provider.getDisplayName())); + } + + /** + * User encountered an error when attempting to sign-in with a provider. + * Note: The user is still on the sign-in activity when this call is made. + * @param provider the provider the user attempted to sign-in with that encountered an error. + * @param ex the exception that occurred. + */ + public void onIntermediateProviderError(Activity callingActivity, IdentityProvider provider, Exception ex) { + final String failureFormatString = callingActivity.getString(R.string.sign_in_failure_message_format); + Log.e(LOG_TAG, String.format(failureFormatString, + provider.getDisplayName(), ex.getMessage()), ex); + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityHandler.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityHandler.java new file mode 100644 index 00000000000..d634728f55d --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +/** + * Allows the application to get an asynchronous response with user's + * unique identifier. + */ +public interface IdentityHandler { + /** + * Handles the user's unique identifier. + * @param identityId Amazon Cognito Identity ID which uniquely identifies + * the user. + */ + void onIdentityId(final String identityId); + + /** + * Handles any error that might have occurred while getting the user's + * unique identifier from Amazon Cognito. + * @param exception exception + */ + void handleError(final Exception exception); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityManager.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityManager.java new file mode 100644 index 00000000000..eca435f3443 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityManager.java @@ -0,0 +1,832 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.SDKGlobalConfiguration; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.auth.AWSBasicCognitoIdentityProvider; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.CognitoCachingCredentialsProvider; + +import com.amazonaws.mobile.auth.core.signin.AuthException; +import com.amazonaws.mobile.auth.core.signin.CognitoAuthException; +import com.amazonaws.mobile.auth.core.signin.ProviderAuthException; +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.SignInProvider; +import com.amazonaws.mobile.auth.core.signin.SignInProviderResultHandler; +import com.amazonaws.mobile.auth.core.internal.util.ThreadUtils; + +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; + +import java.io.IOException; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * The identity manager keeps track of the current sign-in provider and is responsible + * for caching credentials. + */ +public class IdentityManager { + + /** Holder for the credentials provider, allowing the underlying provider to be swapped when necessary. */ + private class AWSCredentialsProviderHolder implements AWSCredentialsProvider { + + /** Reference to the credentials provider. */ + private volatile CognitoCachingCredentialsProvider underlyingProvider; + + @Override + public AWSCredentials getCredentials() { + return underlyingProvider.getCredentials(); + } + + @Override + public void refresh() { + underlyingProvider.refresh(); + } + + private CognitoCachingCredentialsProvider getUnderlyingProvider() { + return underlyingProvider; + } + + private void setUnderlyingProvider(final CognitoCachingCredentialsProvider underlyingProvider) { + // if the current underlyingProvider is not null + this.underlyingProvider = underlyingProvider; + } + } + + /** Log tag. */ + private static final String LOG_TAG = IdentityManager.class.getSimpleName(); + + /** AWS Configuration json file */ + private static final String AWS_CONFIGURATION_FILE = "awsconfiguration.json"; + + /** Holder for the credentials provider, allowing the underlying provider to be swapped when necessary. */ + private final AWSCredentialsProviderHolder credentialsProviderHolder; + + /** Application context. */ + private final Context appContext; + + /** Configuration for the mobile helper. */ + private AWSConfiguration awsConfiguration; + + /* Cognito client configuration. */ + private final ClientConfiguration clientConfiguration; + + /** Executor service for obtaining credentials in a background thread. */ + private final ExecutorService executorService = Executors.newFixedThreadPool(4); + + /** Timeout CountdownLatch for doStartupAuth(). */ + private final CountDownLatch startupAuthTimeoutLatch = new CountDownLatch(1); + + /** Keep track of the registered sign-in providers. */ + private final List> signInProviderClasses + = new LinkedList>(); + + /** Current provider beingIdentityProviderType used to obtain a Cognito access token. */ + private volatile IdentityProvider currentIdentityProvider = null; + + /** Results adapter for adapting results that came from logging in with a provider. */ + private SignInProviderResultAdapter resultsAdapter; + + /** Keep track of the currently registered SignInStateChangeListeners. */ + private final HashSet signInStateChangeListeners + = new HashSet(); + + /** Reference to the default identity manager */ + private static IdentityManager defaultIdentityManager = null; + + /** + * Shared Preferences Name + * This key is used to store credentials in SharedPreferences + * and used by CognitoCachingCredentialsProvider + */ + private static final String SHARED_PREF_NAME = "com.amazonaws.android.auth"; + + /** Shared Preferences Key */ + private static final String EXPIRATION_KEY = "expirationDate"; + + /** + * Custom Cognito Identity Provider to handle refreshing the individual provider's tokens. + */ + private class AWSRefreshingCognitoIdentityProvider extends AWSBasicCognitoIdentityProvider { + + /** Log tag. */ + private final String LOG_TAG = AWSRefreshingCognitoIdentityProvider.class.getSimpleName(); + + public AWSRefreshingCognitoIdentityProvider(final String accountId, + final String identityPoolId, + final ClientConfiguration clientConfiguration, + final Regions regions) { + super(accountId, identityPoolId, clientConfiguration); + // Force refreshing ID provider to use same region as caching credentials provider + this.cib.setRegion(Region.getRegion(regions)); + } + + @Override + public String refresh() { + + Log.d(LOG_TAG, "Refreshing token..."); + + if (currentIdentityProvider != null) { + final String newToken = currentIdentityProvider.refreshToken(); + + getLogins().put(currentIdentityProvider.getCognitoLoginKey(), newToken); + } + return super.refresh(); + } + } + + /** + * Constructor. + * Initializes with the application context and the AWSConfiguration passed in. + * Creates a default ClientConfiguration with the user agent from AWSConfiguration. + * + * @param context the application context. + * @param awsConfiguration the aws configuration. + */ + public IdentityManager(final Context context, + final AWSConfiguration awsConfiguration) { + this.appContext = context.getApplicationContext(); + this.awsConfiguration = awsConfiguration; + this.clientConfiguration = new ClientConfiguration().withUserAgent(awsConfiguration.getUserAgent()); + this.credentialsProviderHolder = new AWSCredentialsProviderHolder(); + initializeCognito(this.appContext, this.clientConfiguration); + } + + /** + * Constructor. + * Initializes with the application context, the AWSConfiguration + * and the ClientConfiguration passed in. + * Read the UserAgent from AWSConfiguration and set in ClientConfiguration. + * + * @param context the application context. + * @param awsConfiguration the aws configuration. + * @param clientConfiguration the client configuration options such as retries and timeouts. + */ + public IdentityManager(final Context context, + final AWSConfiguration awsConfiguration, + final ClientConfiguration clientConfiguration) { + this.appContext = context.getApplicationContext(); + this.awsConfiguration = awsConfiguration; + this.clientConfiguration = clientConfiguration; + + final String userAgent = this.awsConfiguration.getUserAgent(); + String currentUserAgent = this.clientConfiguration.getUserAgent(); + currentUserAgent = currentUserAgent != null ? currentUserAgent : ""; + + if (userAgent != null && userAgent != currentUserAgent) { + this.clientConfiguration.setUserAgent(currentUserAgent.trim() + " " + userAgent); + } + + this.credentialsProviderHolder = new AWSCredentialsProviderHolder(); + initializeCognito(this.appContext, this.clientConfiguration); + } + + /** + * Constructor. + * Initializes with the activity context, application's credentials provider + * that provides the identity and the client configuration. + * + * @param context the application context. + * @param awsConfiguration the aws configuration. + * @param clientConfiguration the client configuration options such as retries and timeouts. + */ + public IdentityManager(final Context context, + final CognitoCachingCredentialsProvider credentialsProvider, + final ClientConfiguration clientConfiguration) { + this.appContext = context.getApplicationContext(); + this.clientConfiguration = clientConfiguration; + this.credentialsProviderHolder = new AWSCredentialsProviderHolder(); + setCredentialsProvider(context, credentialsProvider); + } + + /** + * Return the default IdentityManager + * + * @return defaultIdentityManager The default IdentityManager object + */ + public static IdentityManager getDefaultIdentityManager() { + return defaultIdentityManager; + } + + /** + * Set the IdentityManager object created as the default + * + * @param identityManager The IdentityManager object to be set as the default + */ + public static void setDefaultIdentityManager(IdentityManager identityManager) { + defaultIdentityManager = null; + defaultIdentityManager = identityManager; + } + + /** + * Retrieve the AWSConfiguration object + * + * @return AWSConfiguration Return the reference to the AWSConfiguration object + */ + public AWSConfiguration getConfiguration() { + return this.awsConfiguration; + } + + /** + * Check if the credentials are expired. + * + * @return true if the cached Cognito credentials are expired, otherwise false. + */ + public boolean areCredentialsExpired() { + + final Date credentialsExpirationDate = + credentialsProviderHolder.getUnderlyingProvider().getSessionCredentitalsExpiration(); + + if (credentialsExpirationDate == null) { + Log.d(LOG_TAG, "Credentials are EXPIRED."); + return true; + } + + long currentTime = System.currentTimeMillis() - + (long)(SDKGlobalConfiguration.getGlobalTimeOffset() * 1000); + + final boolean credsAreExpired = + (credentialsExpirationDate.getTime() - currentTime) < 0; + + Log.d(LOG_TAG, "Credentials are " + (credsAreExpired ? "EXPIRED." : "OK")); + + return credsAreExpired; + } + + /** + * Retrieve the reference to AWSCredentialsProvider object. + * + * @return the Cognito credentials provider. + */ + public AWSCredentialsProvider getCredentialsProvider() { + return this.credentialsProviderHolder; + } + + /** + * Retrieve the reference to CognitoCachingCredentialsProvider object. + * + * @return the Cognito Caching Credentials Provider + */ + public CognitoCachingCredentialsProvider getUnderlyingProvider() { + return this.credentialsProviderHolder.getUnderlyingProvider(); + } + + /** + * Gets the cached unique identifier for the user. + * + * @return the cached unique identifier for the user. + */ + public String getCachedUserID() { + return credentialsProviderHolder.getUnderlyingProvider().getCachedIdentityId(); + } + + /** + * Gets the user's unique identifier. This method can be called from + * any thread. + * + * @param handler handles the unique identifier for the user + */ + public void getUserID(final IdentityHandler handler) { + + executorService.submit(new Runnable() { + Exception exception = null; + + @Override + public void run() { + String identityId = null; + + try { + // Retrieve the user identity on the background thread. + identityId = credentialsProviderHolder.getUnderlyingProvider().getIdentityId(); + } catch (final Exception exception) { + this.exception = exception; + Log.e(LOG_TAG, exception.getMessage(), exception); + } finally { + final String result = identityId; + Log.d(LOG_TAG, "Got user ID: " + identityId); + + // Lint doesn't like early return inside a finally block, so nesting further inside the if here. + if (handler != null) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + if (exception != null) { + handler.handleError(exception); + return; + } + + handler.onIdentityId(result); + } + }); + } + } + } + }); + } + + /** + * The adapter to handle results that come back from Cognito as well as handle the result from + * any login providers. + */ + private class SignInProviderResultAdapter implements SignInProviderResultHandler { + final private SignInProviderResultHandler handler; + + private SignInProviderResultAdapter(final SignInProviderResultHandler handler) { + this.handler = handler; + } + + @Override + public void onSuccess(final IdentityProvider provider) { + Log.d(LOG_TAG, + String.format("SignInProviderResultAdapter.onSuccess(): %s provider sign-in succeeded.", + provider.getDisplayName())); + // Update Cognito login with the token. + federateWithProvider(provider); + } + + private void onCognitoSuccess() { + Log.d(LOG_TAG, "SignInProviderResultAdapter.onCognitoSuccess()"); + handler.onSuccess(currentIdentityProvider); + } + + private void onCognitoError(final Exception ex) { + Log.d(LOG_TAG, "SignInProviderResultAdapter.onCognitoError()", ex); + final IdentityProvider provider = currentIdentityProvider; + // Sign out of parent provider. This clears the currentIdentityProvider. + IdentityManager.this.signOut(); + handler.onError(provider, new CognitoAuthException(provider, ex)); + } + + @Override + public void onCancel(final IdentityProvider provider) { + Log.d(LOG_TAG, String.format( + "SignInProviderResultAdapter.onCancel(): %s provider sign-in canceled.", + provider.getDisplayName())); + handler.onCancel(provider); + } + + @Override + public void onError(final IdentityProvider provider, final Exception ex) { + Log.e(LOG_TAG, + String.format("SignInProviderResultAdapter.onError(): %s provider error. %s", + provider.getDisplayName(), ex.getMessage()), ex); + handler.onError(provider, new ProviderAuthException(provider, ex)); + } + } + + /** + * Add a listener to receive callbacks when sign-in or sign-out occur. The listener + * methods will always be called on a background thread. + * + * @param listener the sign-in state change listener. + */ + public void addSignInStateChangeListener(final SignInStateChangeListener listener) { + synchronized (signInStateChangeListeners) { + signInStateChangeListeners.add(listener); + } + } + + /** + * Remove a listener from receiving callbacks when sign-in or sign-out occur. + * + * @param listener the sign-in state change listener. + */ + public void removeSignInStateChangeListener(final SignInStateChangeListener listener) { + synchronized (signInStateChangeListeners) { + signInStateChangeListeners.remove(listener); + } + } + + /** + * Call getResultsAdapter to get the IdentityManager's handler that adapts results before + * sending them back to the handler set by {@link #setProviderResultsHandler(SignInProviderResultHandler)} + * + * @return the Identity Manager's results adapter. + */ + public SignInProviderResultAdapter getResultsAdapter() { + return resultsAdapter; + } + + /** + * Sign out of the current identity provider, and clear Cognito credentials. + * Note: This call does not attempt to obtain un-auth credentials. To obtain an unauthenticated + * anonymous (guest) identity, call {@link #getUserID(IdentityHandler)}. + */ + public void signOut() { + Log.d(LOG_TAG, "Signing out..."); + + if (currentIdentityProvider != null) { + executorService.submit(new Runnable() { + @Override + public void run() { + currentIdentityProvider.signOut(); + credentialsProviderHolder.getUnderlyingProvider().clear(); + currentIdentityProvider = null; + + // Notify state change listeners of sign out. + synchronized (signInStateChangeListeners) { + for (final SignInStateChangeListener listener : signInStateChangeListeners) { + listener.onUserSignedOut(); + } + } + } + }); + } + } + + private void refreshCredentialWithLogins(final Map loginMap) { + + final CognitoCachingCredentialsProvider credentialsProvider = + credentialsProviderHolder.getUnderlyingProvider(); + credentialsProvider.clear(); + credentialsProvider.withLogins(loginMap); + + // Calling refresh is equivalent to calling getIdentityId() + getCredentials(). + Log.d(LOG_TAG, "refresh credentials"); + credentialsProvider.refresh(); + + // expire credentials in 2 minutes. + appContext.getSharedPreferences(SHARED_PREF_NAME, + Context.MODE_PRIVATE).edit() + .putLong(credentialsProvider.getIdentityPoolId() + "." + EXPIRATION_KEY, System.currentTimeMillis() + (510*1000)) + .commit(); + + } + + /** + * Set the results handler that will be used for results when calling federateWithProvider. + * + * @param signInProviderResultHandler the results handler. + */ + public void setProviderResultsHandler(final SignInProviderResultHandler signInProviderResultHandler) { + if (signInProviderResultHandler == null) { + throw new IllegalArgumentException("signInProviderResultHandler cannot be null."); + } + this.resultsAdapter = new SignInProviderResultAdapter(signInProviderResultHandler); + } + + /** + * Login with an identity provider (ie. Facebook, Twitter, etc.). + * + * @param provider A sign-in provider. + */ + public void federateWithProvider(final IdentityProvider provider) { + Log.d(LOG_TAG, "federateWithProvider"); + final Map loginMap = new HashMap(); + loginMap.put(provider.getCognitoLoginKey(), provider.getToken()); + currentIdentityProvider = provider; + initializeCognito(this.appContext, this.clientConfiguration); + + executorService.submit(new Runnable() { + @Override + public void run() { + try { + refreshCredentialWithLogins(loginMap); + } catch (Exception ex) { + resultsAdapter.onCognitoError(ex); + return; + } + + resultsAdapter.onCognitoSuccess(); + + // Notify state change listeners of sign out. + synchronized (signInStateChangeListeners) { + for (final SignInStateChangeListener listener : signInStateChangeListeners) { + listener.onUserSignedIn(); + } + } + } + }); + } + + /** + * Gets the current provider. + * + * @return current provider or null if not signed-in + */ + public IdentityProvider getCurrentIdentityProvider() { + return currentIdentityProvider; + } + + /** + * Add a supported identity provider to your app. + * The provider will be presented as option to sign in to your app. + * + * @param providerClass the provider class for the identity provider. + */ + public void addSignInProvider(final Class providerClass) { + signInProviderClasses.add(providerClass); + } + + /** + * Gets the list of SignInProvider classes + * + * @return list of the signInProvider classes + */ + public Collection> getSignInProviderClasses() { + return signInProviderClasses; + } + + /** + * Check if user is signed in. + * + * @return true if Cognito credentials have been obtained with at least one provider. + */ + public boolean isUserSignedIn() { + final Map logins = credentialsProviderHolder.getUnderlyingProvider().getLogins(); + if (logins == null || logins.size() == 0) + return false; + return true; + } + + private void handleStartupAuthResult(final Activity callingActivity, + final StartupAuthResultHandler startupAuthResultHandler, + final AuthException authException, + final Exception unAuthException) { + runAfterStartupAuthDelay(callingActivity, new Runnable() { + @Override + public void run() { + startupAuthResultHandler.onComplete(new StartupAuthResult(IdentityManager.this, + new StartupAuthErrorDetails(authException, unAuthException))); + } + }); + } + + private void handleUnauthenticated(final Activity callingActivity, + final StartupAuthResultHandler startupAuthResultHandler, + final AuthException ex) { + // Optional Sign-in can dispose the sign-in manager right away here, while mandatory sign-in, needs + // it to stay around a bit longer, since it will be required to call sign-in or sign-up. + + SignInManager.dispose(); + + if (getCachedUserID() != null) { + handleStartupAuthResult(callingActivity, startupAuthResultHandler, ex, null); + } + + getUserID(new IdentityHandler() { + @Override + public void onIdentityId(final String identityId) { + handleStartupAuthResult(callingActivity, startupAuthResultHandler, ex, null); + } + + @Override + public void handleError(final Exception exception) { + handleStartupAuthResult(callingActivity, startupAuthResultHandler, ex, exception); + } + }); + handleStartupAuthResult(callingActivity, startupAuthResultHandler, ex, null); + } + + /** + * Starts an activity after the splash timeout. + * + * @param runnable runnable to run after the splash timeout expires. + */ + private void runAfterStartupAuthDelay(final Activity callingActivity, final Runnable runnable) { + executorService.submit(new Runnable() { + public void run() { + // Wait for the splash timeout expiry or for the user to tap. + try { + startupAuthTimeoutLatch.await(); + } catch (InterruptedException e) { + Log.d(LOG_TAG, "Interrupted while waiting for startup auth minimum delay."); + } + + callingActivity.runOnUiThread(runnable); + } + }); + } + + /** + * This should be called from your app's splash activity upon start-up. If the user was previously + * signed in, this will attempt to refresh their identity using the previously sign-ed in provider. + * If the user was not previously signed in or their identity could not be refreshed with the + * previously signed in provider and sign-in is optional, it will attempt to obtain an unauthenticated (guest) + * identity. + * + * @param callingActivity the calling activity. + * @param startupAuthResultHandler a handler for returning results. + * @param minimumDelay the minimum delay to wait before returning the sign-in result. + */ + public void doStartupAuth(final Activity callingActivity, + final StartupAuthResultHandler startupAuthResultHandler, + final long minimumDelay) { + + executorService.submit(new Runnable() { + public void run() { + Log.d(LOG_TAG, "Starting up authentication..."); + final SignInManager signInManager = SignInManager.getInstance( + callingActivity.getApplicationContext()); + + if (signInManager == null) { + throw new IllegalStateException("You cannot pass null for identityManager."); + } + + final SignInProvider provider = signInManager.getPreviouslySignedInProvider(); + + // if the user was already previously signed-in to a provider. + if (provider != null) { + Log.d(LOG_TAG, "Refreshing credentials with identity provider " + provider.getDisplayName()); + // asynchronously handle refreshing credentials and call our handler. + signInManager.refreshCredentialsWithProvider(callingActivity, + provider, new SignInProviderResultHandler() { + + @Override + public void onSuccess(final IdentityProvider provider) { + // The sign-in manager is no longer needed once signed in. + SignInManager.dispose(); + + Log.d(LOG_TAG, "Successfully got credentials from identity provider '" + + provider.getDisplayName()); + + runAfterStartupAuthDelay(callingActivity, new Runnable() { + @Override + public void run() { + startupAuthResultHandler.onComplete(new StartupAuthResult(IdentityManager.this, null)); + } + }); + } + + @Override + public void onCancel(final IdentityProvider provider) { + // Should never happen. + Log.wtf(LOG_TAG, "Cancel can't happen when handling a previously signed-in user."); + } + + @Override + public void onError(final IdentityProvider provider, final Exception ex) { + Log.e(LOG_TAG, + String.format("Cognito credentials refresh with %s provider failed. Error: %s", + provider.getDisplayName(), ex.getMessage()), ex); + + if (ex instanceof AuthException) { + handleUnauthenticated(callingActivity, startupAuthResultHandler, + (AuthException) ex); + } else { + handleUnauthenticated(callingActivity, startupAuthResultHandler, + new AuthException(provider, ex)); + } + } + }); + } else { + handleUnauthenticated(callingActivity, startupAuthResultHandler, null); + } + + if (minimumDelay > 0) { + // Wait for the splash timeout. + try { + Thread.sleep(minimumDelay); + } catch (final InterruptedException ex) { + Log.i(LOG_TAG, "Interrupted while waiting for startup auth timeout."); + } + } + + // Expire the splash page delay. + startupAuthTimeoutLatch.countDown(); + } + }); + } + + /** + * This should be called from your app's splash activity upon start-up. If the user was previously + * signed in, this will attempt to refresh their identity using the previously sign-ed in provider. + * If the user was not previously signed in or their identity could not be refreshed with the + * previously signed in provider and sign-in is optional, it will attempt to obtain an unauthenticated (guest) + * identity. + * + * @param callingActivity the calling activity. + * @param startupAuthResultHandler a handler for returning results. + */ + public void doStartupAuth(final Activity callingActivity, + final StartupAuthResultHandler startupAuthResultHandler) { + doStartupAuth(callingActivity, startupAuthResultHandler, 0); + } + + /** + * Call this to ignore waiting for the remaining timeout delay. + */ + public void expireSignInTimeout() { + startupAuthTimeoutLatch.countDown(); + } + + /** + * Call setUpToAuthenticate to initiate sign-in with a provider. + * + * Note: This should not be called when already signed in with a provider. + * + * @param context context. + * @param signInResultHandler the results handler. + */ + public void setUpToAuthenticate(final Context context, + final SignInResultHandler signInResultHandler) { + // Start the sign-in activity. We do not finish the calling activity allowing the user to navigate back. + final SignInManager signInManager = SignInManager.getInstance( + context.getApplicationContext()); + signInManager.setResultHandler(signInResultHandler); + } + + private void setCredentialsProvider(final Context context, + final CognitoCachingCredentialsProvider cachingCredentialsProvider) { + credentialsProviderHolder.setUnderlyingProvider(cachingCredentialsProvider); + } + + /** + * The CognitoCachingCredentialProvider loads cached credentials when it is + * instantiated, however, it does not reload the login map, which must be reloaded + * in order to refresh the credentials. Therefore, currently cached credentials are + * only useful for unauthenticated users. + */ + private void initializeCognito(final Context context, + final ClientConfiguration clientConfiguration) { + + Log.d(LOG_TAG, "Initializing Cognito"); + + final String region = getCognitoIdentityRegion(); + Regions cognitoIdentityRegion = Regions.fromName(region); + + setCredentialsProvider(context, + new CognitoCachingCredentialsProvider(context, getCognitoIdentityPoolId(), + cognitoIdentityRegion, clientConfiguration)); + + final AWSRefreshingCognitoIdentityProvider refreshingCredentialsProvider = + new AWSRefreshingCognitoIdentityProvider(null, getCognitoIdentityPoolId(), + clientConfiguration, cognitoIdentityRegion); + + setCredentialsProvider(context, + new CognitoCachingCredentialsProvider(context, refreshingCredentialsProvider, + cognitoIdentityRegion, clientConfiguration)); + } + + /** + * Retrieve the Cognito IdentityPooldId from CognitoIdentity -> PoolId key + * + * @return PoolId + * @throws IllegalArgumentException + */ + private String getCognitoIdentityPoolId() throws IllegalArgumentException { + try { + return this.awsConfiguration + .optJsonObject("CredentialsProvider") + .getJSONObject("CognitoIdentity") + .getJSONObject(this.awsConfiguration.getConfiguration()) + .getString("PoolId"); + } catch (Exception exception) { + throw new IllegalArgumentException("Cannot access Cognito IdentityPoolId from the " + + AWS_CONFIGURATION_FILE + " file.", exception); + } + } + + /** + * Retrieve the Cognito Region from CognitoIdentity -> Region key + * + * @return CognitoIdentity Region + * @throws IllegalArgumentException + */ + private String getCognitoIdentityRegion() throws IllegalArgumentException { + try { + return this.awsConfiguration + .optJsonObject("CredentialsProvider") + .getJSONObject("CognitoIdentity") + .getJSONObject(this.awsConfiguration.getConfiguration()) + .getString("Region"); + } catch (Exception exception) { + throw new IllegalArgumentException("Cannot find the Cognito Region from the " + + AWS_CONFIGURATION_FILE + " file.", exception); + } + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityProvider.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityProvider.java new file mode 100644 index 00000000000..5602a8c1d3f --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/IdentityProvider.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +import android.content.Context; + +import com.amazonaws.mobile.config.AWSConfiguration; + +/** + * Interface sign-in provider's supported by the IdentityManager must implement. + */ +public interface IdentityProvider { + + /** + * Method called upon constructing an identity provider for it to handle its initialization. + * + * @param context the context. + * @param configuration the configuration. + */ + void initialize(Context context, AWSConfiguration configuration); + + /** + * @return the display name for this provider. + */ + String getDisplayName(); + + /** + * @return the key used by Cognito in its login map when refreshing credentials. + */ + String getCognitoLoginKey(); + + /** + * Refreshes the state of whether the user is signed-in and returns the updated state. + * Note: This call may block, so it must not be called from the main thread. + * @return true if signed in with this provider, otherwise false. + */ + boolean refreshUserSignInState(); + + /** + * Call getToken to retrieve the access token from successful sign-in with this provider. + * Note: This call may block if the access token is not already cached. + * @return the access token suitable for use with Cognito. + */ + String getToken(); + + /** + * Refreshes the token if it has expired. + * Note: this call may block due to network access, and must be called from a background thread. + * @return the refreshed access token, or null if the token cannot be refreshed. + */ + String refreshToken(); + + /** + * Call signOut to sign out of this provider. + */ + void signOut(); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInResultHandler.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInResultHandler.java new file mode 100644 index 00000000000..c2f67b704d2 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInResultHandler.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +import android.app.Activity; + +/** + * Interface for handling results from calling IdentityManager's signInOrSignUp(). + */ +public interface SignInResultHandler { + /** + * Called when the user has obtained an identity by signing in with a provider. + * + * @param callingActivity the calling activity that should be finished. + * @param provider the provider or null if succeeded with an unauthenticated identity. + */ + void onSuccess(Activity callingActivity, IdentityProvider provider); + + /** + * User cancelled signing in with a provider on the sign-in activity. + * Note: The user is still on the sign-in activity when this call is made. + * @param provider the provider the user canceled with. + */ + void onIntermediateProviderCancel(Activity callingActivity, IdentityProvider provider); + + /** + * User encountered an error when attempting to sign-in with a provider. + * Note: The user is still on the sign-in activity when this call is made. + * @param provider the provider the user attempted to sign-in with that encountered an error. + * @param ex the exception that occurred. + */ + void onIntermediateProviderError(Activity callingActivity, IdentityProvider provider, Exception ex); + + /** + * User pressed back from the sign-in Activity. + * + * @return true if the activity should be finished, otherwise false. + */ + boolean onCancel(Activity callingActivity); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInStateChangeListener.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInStateChangeListener.java new file mode 100644 index 00000000000..1d1012ebd8b --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/SignInStateChangeListener.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +/** + * Implement this interface to receive callbacks when the user's sign-in state changes + * from signed-in to not signed-in or vice versa. + */ +public interface SignInStateChangeListener { + + /** + * Invoked when the user completes sign-in. + */ + void onUserSignedIn(); + + /** + * Invoked when the user signs out. + */ + void onUserSignedOut(); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthErrorDetails.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthErrorDetails.java new file mode 100644 index 00000000000..b4f0a88c688 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthErrorDetails.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +import com.amazonaws.mobile.auth.core.signin.AuthException; + +/** + * Encapsulates errors that may have happened during doStartupAuth(). + */ +public class StartupAuthErrorDetails { + private final AuthException authException; + private final Exception unauthException; + + public StartupAuthErrorDetails(final AuthException authException, + final Exception unauthException) { + this.authException = authException; + this.unauthException = unauthException; + } + + /** + * @return true if an error occurred refreshing a previously signed in provider, otherwise false. + */ + public boolean didErrorOccurRefreshingProvider() { + return authException != null; + } + + /** + * @return the AuthException that occurred while refreshing a provider, otherwise null. + */ + public AuthException getProviderRefreshException() { + return authException; + } + + /** + * @return true if an error occurred obtaining an unauthenticated identity, otherwise false. + */ + public boolean didErrorOccurObtainingUnauthenticatedIdentity() { + return unauthException != null; + } + + /** + * @return the exception that occurred while trying to obtain an unauthenticated (guest) identity. + */ + public Exception getUnauthenticatedErrorException() { + return unauthException; + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResult.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResult.java new file mode 100644 index 00000000000..35d47c28fa9 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResult.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +/** + * The result for IdentityManager's doStartupAuth(). + */ +public class StartupAuthResult { + private final IdentityManager identityManager; + private final StartupAuthErrorDetails errors; + + public StartupAuthResult(final IdentityManager identityManager, + final StartupAuthErrorDetails startupAuthErrorDetails) { + this.identityManager = identityManager; + this.errors = startupAuthErrorDetails; + } + + /** + * return true if signed in with an identity provider, otherwise false if signed in as an + * unauthenticated (guest) identity or not signed in at all. + */ + public boolean isUserSignedIn() { + return identityManager.isUserSignedIn(); + } + /** + * @return true if an unauthenticated (guest) identity was obtained, otherwise false. + */ + public boolean isUserAnonymous() { + return didObtainIdentity() && !isUserSignedIn(); + } + + /** + * @return true if an identity was obtained, either unauthenticated (guest) or authenticated with a provider. + */ + private boolean didObtainIdentity() { + return identityManager.getCachedUserID() != null; + } + + /** + * @return the identity manager. + */ + public IdentityManager getIdentityManager() { + return identityManager; + } + + /** + * @return StartupAuthErrorDetails object if errors occurred during the StartupAuthResult flow, otherwise null. + */ + public StartupAuthErrorDetails getErrorDetails() { + return errors; + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResultHandler.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResultHandler.java new file mode 100644 index 00000000000..c16eba7a5b1 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/StartupAuthResultHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; + +/** + * Interface for calling IdentityManager's doStartupAuth(). + */ +public interface StartupAuthResultHandler { + /** + * Called when the startup auth flow is complete. + * For Optional Sign-in one of the following occurred: + * 1. No identity was obtained. + * 2. An unauthenticated (guest) identity was obtained. + * 3. An authenticated identity was obtained (using an identity provider). + * + * @param authResults the StartupAuthResult object containing the results for doStartupAuth(). + */ + void onComplete(StartupAuthResult authResults); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ThreadUtils.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ThreadUtils.java new file mode 100644 index 00000000000..a044ff5bcda --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ThreadUtils.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.internal.util; + +import android.os.Handler; +import android.os.Looper; + +public class ThreadUtils { + + private ThreadUtils() { + + } + + /** + * Run a runnable on the Main (UI) Thread. + * @param runnable the runnable + */ + public static void runOnUiThread(final Runnable runnable) { + if (Looper.myLooper() != Looper.getMainLooper()) { + new Handler(Looper.getMainLooper()).post(runnable); + } else { + runnable.run(); + } + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ViewHelper.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ViewHelper.java new file mode 100644 index 00000000000..2b3064e8c74 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/ViewHelper.java @@ -0,0 +1,78 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.internal.util; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.widget.EditText; + +/** + * Utilities for Views. + * + */ +public final class ViewHelper { + /** + * Displays a modal dialog with an OK button. + * + * @param activity invoking activity + * @param title title to display for the dialog + * @param body content of the dialog + */ + public static void showDialog(final Activity activity, final String title, final String body) { + if (null == activity) { + return; + } + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(title); + builder.setMessage(body); + builder.setNeutralButton(android.R.string.ok, null); + builder.show(); + } + + /** + * Displays a modal dialog. + * + * @param activity invoking activity + * @param title title to display for the dialog + * @param body content of the dialog + * @param positiveButton String for positive button + * @param negativeButton String for negative button + * @param negativeButtonListener the listener which should be invoked when a negative button is pressed + * @param positiveButtonListener the listener which should be invoked when a positive button is pressed + */ + public static void showDialog(final Activity activity, + final String title, + final String body, + final String positiveButton, + final DialogInterface.OnClickListener positiveButtonListener, + final String negativeButton, + final DialogInterface.OnClickListener negativeButtonListener) { + if (null == activity) { + return; + } + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(title); + builder.setMessage(body); + builder.setPositiveButton(positiveButton,positiveButtonListener); + builder.setNegativeButton(negativeButton, negativeButtonListener); + builder.show(); + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/package-info.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/package-info.java new file mode 100644 index 00000000000..b7b98e15b38 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/internal/util/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.internal.util; \ No newline at end of file diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/package-info.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/package-info.java new file mode 100644 index 00000000000..fc66e48970a --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core; \ No newline at end of file diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/AuthException.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/AuthException.java new file mode 100644 index 00000000000..2f093010be9 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/AuthException.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +import com.amazonaws.mobile.auth.core.IdentityProvider; + +/** + * Base class for exceptions that occur during auth + */ +public class AuthException extends Exception { + protected final IdentityProvider provider; + + /** + * Constructor. + * @param provider the auth provider that was being used. + * @param ex the exception that occurred. + */ + public AuthException(final IdentityProvider provider, final Exception ex) { + super(ex); + this.provider = provider; + } + + /** + * Constructor. + * @param ex the exception that occurred. + */ + public AuthException(final Exception ex) { + this(null, ex); + } + + /** + * @return the provider that was used when the failure occurred, or null if no provider + * was being used when the auth exception occurred. + */ + public IdentityProvider getProvider() { + return provider; + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/CognitoAuthException.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/CognitoAuthException.java new file mode 100644 index 00000000000..fb558b28144 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/CognitoAuthException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +import com.amazonaws.mobile.auth.core.IdentityProvider; + +/** + * Thrown when there is an error obtaining a Cognito identity using an identity provider's token + * during the start-up authentication flow or when signing in with a provider. + */ +public class CognitoAuthException extends ProviderAuthException { + /** + * Constructor. + * @param provider the provider that was used while attempting to obtain a Cognito identity. + * @param ex the exception that occurred while attempting to obtain the Cognito identity. + */ + public CognitoAuthException(final IdentityProvider provider, final Exception ex) { + super(provider, ex); + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ProviderAuthException.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ProviderAuthException.java new file mode 100644 index 00000000000..8c8fd6ed6d1 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ProviderAuthException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +import com.amazonaws.mobile.auth.core.IdentityProvider; + +/** + * Thrown when there is an error with a provider during the start-up authentication + * flow or when signing in with a provider. + */ +public class ProviderAuthException extends AuthException { + /** + * Constructor. + * @param provider the provider that was used while attempting to obtain a Cognito identity. + * @param ex the exception that occurred while attempting to obtain the Cognito identity. + */ + public ProviderAuthException(final IdentityProvider provider, final Exception ex) { + super(provider, ex); + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInManager.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInManager.java new file mode 100644 index 00000000000..9a4086fff1c --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInManager.java @@ -0,0 +1,308 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.util.SparseArray; +import android.view.View; + +import com.amazonaws.mobile.auth.core.IdentityManager; +import com.amazonaws.mobile.auth.core.IdentityProvider; +import com.amazonaws.mobile.auth.core.SignInResultHandler; +import com.amazonaws.mobile.auth.core.internal.util.ThreadUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * The SignInManager is a singleton component, which orchestrates sign-in and sign-up flows. It is responsible for + * discovering the previously signed-in provider and that provider's credentials, as well as initializing sign-in + * buttons with the providers. + */ +public class SignInManager { + /** Log Tag */ + private static final String LOG_TAG = SignInManager.class.getSimpleName(); + + /** This map holds the class and the object of the signin providers */ + private final Map, SignInProvider> signInProviders + = new HashMap, SignInProvider>(); + + /** Holds the Singleton instance */ + private volatile static SignInManager singleton = null; + + /** Reference to user's completion handler */ + private volatile SignInResultHandler signInResultHandler; + + /** permissions handler */ + private final SparseArray providersHandlingPermissions + = new SparseArray(); + + /** + * Constructor. + */ + private SignInManager(final Context context) { + if (singleton != null) { + throw new AssertionError(); + } + + for (Class providerClass : IdentityManager.getDefaultIdentityManager().getSignInProviderClasses()) { + final SignInProvider provider; + try { + provider = providerClass.newInstance(); + } catch (final IllegalAccessException ex) { + throw new RuntimeException(ex); + } catch (final InstantiationException ex) { + throw new RuntimeException(ex); + } + provider.initialize(context, IdentityManager.getDefaultIdentityManager().getConfiguration()); + + this.signInProviders.put(providerClass, provider); + + if (provider instanceof SignInPermissionsHandler) { + final SignInPermissionsHandler handler = (SignInPermissionsHandler) provider; + providersHandlingPermissions.put(handler.getPermissionRequestCode(), handler); + } + } + + singleton = this; + } + + /** + * Gets the singleton instance of this class. + * + * @return instance + */ + public synchronized static SignInManager getInstance() { + return singleton; + } + + /** + * Gets the singleton instance of this class. + * + * @return instance + */ + public synchronized static SignInManager getInstance(final Context context) { + if (singleton == null) { + singleton = new SignInManager(context); + } + return singleton; + } + + /** + * Set the passed in SignInResultHandler + * + * @param signInResultHandler + */ + public void setResultHandler(final SignInResultHandler signInResultHandler) { + this.signInResultHandler = signInResultHandler; + } + + /** + * Retrieve the reference to SignInResultHandler + * + * @return SignInResultHandler + */ + public SignInResultHandler getResultHandler() { + return signInResultHandler; + } + + /** + * Dispose the SignInManager + */ + public synchronized static void dispose() { + singleton = null; + } + + /** + * Call getPreviouslySignedInProvider to determine if the user was left signed-in when the app + * was last running. This should be called on a background thread since it may perform file + * i/o. If the user is signed in with a provider, this will return the provider for which the + * user is signed in. Subsequently, refreshCredentialsWithProvider should be called with the + * provider returned from this method. + * + * @return false if not already signed in, true if the user was signed in with a provider. + */ + public SignInProvider getPreviouslySignedInProvider() { + + for (final SignInProvider provider : signInProviders.values()) { + // Note: This method may block. This loop could potentially be sped + // up by running these calls in parallel using an executorService. + if (provider.refreshUserSignInState()) { + return provider; + } + } + return null; + } + + private class SignInProviderResultAdapter implements SignInProviderResultHandler { + final private SignInProviderResultHandler handler; + final private Activity activity; + + private SignInProviderResultAdapter(final Activity activity, + final SignInProviderResultHandler handler) { + this.handler = handler; + this.activity = activity; + } + + private Activity getActivity() { + return activity; + } + + /** {@inheritDoc} */ + @Override + public void onSuccess(final IdentityProvider provider) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + handler.onSuccess(provider); + } + }); + } + + /** {@inheritDoc} */ + @Override + public void onCancel(final IdentityProvider provider) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + handler.onCancel(provider); + } + }); + } + + /** {@inheritDoc} */ + @Override + public void onError(final IdentityProvider provider, final Exception ex) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + handler.onError(provider, ex); + } + }); + } + } + + private SignInProviderResultAdapter resultsAdapter; + + /** + * Refresh Cognito credentials with a provider. Results handlers are always called on the main + * thread. + * + * @param activity the calling activity. + * @param provider the sign-in provider that was previously signed in. + * @param resultsHandler the handler to receive results for credential refresh. + */ + public void refreshCredentialsWithProvider(final Activity activity, + final IdentityProvider provider, + final SignInProviderResultHandler resultsHandler) { + + if (provider == null) { + throw new IllegalArgumentException("The sign-in provider cannot be null."); + } + + if (provider.getToken() == null) { + resultsHandler.onError(provider, + new IllegalArgumentException("Given provider not previously logged in.")); + } + + resultsAdapter = new SignInProviderResultAdapter(activity, resultsHandler); + IdentityManager.getDefaultIdentityManager().setProviderResultsHandler(resultsAdapter); + IdentityManager.getDefaultIdentityManager().federateWithProvider(provider); + } + + /** + * Sets the results handler results from sign-in with a provider. Results handlers are + * always called on the UI thread. + * + * @param activity the calling activity. + * @param resultsHandler the handler for results from sign-in with a provider. + */ + public void setProviderResultsHandler(final Activity activity, + final SignInProviderResultHandler resultsHandler) { + resultsAdapter = new SignInProviderResultAdapter(activity, resultsHandler); + // Set the final results handler with the identity manager. + IdentityManager.getDefaultIdentityManager().setProviderResultsHandler(resultsAdapter); + } + + /** + * Call initializeSignInButton to initialize the logic for sign-in for a specific provider. + * + * @param providerType the SignInProvider class. + * @param buttonView the view for the button associated with this provider. + * @return the onClickListener for the button to be able to override the listener. + */ + public View.OnClickListener initializeSignInButton(final Class providerClass, + final View buttonView) { + final SignInProvider provider = findProvider(providerClass); + + // Initialize the sign in button with the identity manager's results adapter. + return provider.initializeSignInButton(resultsAdapter.getActivity(), + buttonView, IdentityManager.getDefaultIdentityManager().getResultsAdapter()); + } + + private SignInProvider findProvider(final Class providerClass) { + + final SignInProvider provider = signInProviders.get(providerClass); + + if (provider == null) { + throw new IllegalArgumentException("No such provider : " + providerClass.getName()); + } + + return provider; + } + + /** + * Handle the Activity result for login providers. + * + * @param requestCode the request code. + * @param resultCode the result code. + * @param data result intent. + * @return true if the sign-in manager handle the result, otherwise false. + */ + public boolean handleActivityResult(final int requestCode, + final int resultCode, + final Intent data) { + for (final SignInProvider provider : signInProviders.values()) { + if (provider.isRequestCodeOurs(requestCode)) { + provider.handleActivityResult(requestCode, resultCode, data); + return true; + } + } + + return false; + } + + /** + * Handle the Activity request permissions result for sign-in providers. + * + * @param requestCode the request code. + * @param permissions the permissions requested. + * @param grantResults the grant results. + */ + public void handleRequestPermissionsResult(final int requestCode, + final String permissions[], + final int[] grantResults) { + final SignInPermissionsHandler handler = providersHandlingPermissions.get(requestCode); + if (handler != null) { + handler.handleRequestPermissionsResult(requestCode, permissions, grantResults); + } + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInPermissionsHandler.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInPermissionsHandler.java new file mode 100644 index 00000000000..35afb19946f --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInPermissionsHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +/** + * Interface for handling permissions + */ +public interface SignInPermissionsHandler { + /** + * @return the permission request code that will be used by this provider. + */ + int getPermissionRequestCode(); + + /** + * Handler for permissions results. + * + * @param requestCode the permissions request code + * @param permissions the permissions requested + * @param grantResults the results. + */ + void handleRequestPermissionsResult(int requestCode, String permissions[], + int[] grantResults); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProvider.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProvider.java new file mode 100644 index 00000000000..0f348f3982c --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProvider.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +import android.app.Activity; +import android.content.Intent; +import android.view.View; + +import com.amazonaws.mobile.auth.core.IdentityProvider; + +/** + * Each sign-in provider implements this interface, in order to do sign-in button + * initialization and to handle activity results that have been passed back to the + * app, after a sign-in provider window has been dismissed. + */ +public interface SignInProvider extends IdentityProvider { + /** + * Call isRequestCodeOurs to determine if this provider should handle an activity result. + * @param requestCode the requestCode from a previous call to onActivityResult. + * @return true if the request code is from this provider, otherwise false. + */ + boolean isRequestCodeOurs(int requestCode); + + /** + * Call handleActivityResult to handle the activity result. + * @param requestCode the request code. + * @param resultCode the result code. + * @param data the result intent. + */ + void handleActivityResult(int requestCode, int resultCode, Intent data); + + /** + * Initialize the sign-in button for the sign-in activity. + * @param signInActivity the activity for sign-in. + * @param buttonView the view for the sign-in button to initialize. + * @param resultsHandler the resultsHandler for provider sign-in. + * @return the onClickListener for the button to be able to override the listener, + * and null if the button cannot be initialized. + */ + View.OnClickListener initializeSignInButton(Activity signInActivity, + View buttonView, + SignInProviderResultHandler resultsHandler); +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProviderResultHandler.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProviderResultHandler.java new file mode 100644 index 00000000000..a7bd5d921dd --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/SignInProviderResultHandler.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin; + +import com.amazonaws.mobile.auth.core.IdentityProvider; + +/** + * Implement this interface to get callbacks for the results to a sign-in operation. + */ +public interface SignInProviderResultHandler { + + /** + * Sign-in was successful. + * + * @param provider sign-in identity provider + */ + void onSuccess(IdentityProvider provider); + + /** + * Sign-in was cancelled by the user. + * + * @param provider sign-in identity provider + */ + void onCancel(IdentityProvider provider); + + /** + * Sign-in failed. + * + * @param provider sign-in identity provider + * @param ex exception that occurred + */ + void onError(IdentityProvider provider, Exception ex); +} \ No newline at end of file diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/DisplayUtils.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/DisplayUtils.java new file mode 100644 index 00000000000..60a9f1a85a6 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/DisplayUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin.ui; + +import android.content.res.Resources; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.graphics.drawable.shapes.Shape; +import android.util.DisplayMetrics; + +/** + * A class containing UI Utility methods. + */ +public class DisplayUtils { + private static final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics(); + private static int dpMultiplier = (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); + + /** + * @param dp number of design pixels. + * @return number of pixels corresponding to the desired design pixels. + */ + public static int dp(final int dp) { + return dp * dpMultiplier; + } + + /** + * Create a rounded rectangle with a specified corner radius. + * + * @param cornerRadius the corner radius in pixels + * @return the shape drawable. + */ + public static Shape getRoundedRectangleShape(int cornerRadius) { + + // Background color for Button. + return new RoundRectShape( + new float[]{ + cornerRadius, cornerRadius, cornerRadius, cornerRadius, + cornerRadius, cornerRadius, cornerRadius, cornerRadius}, + null, null); + } + + public static ShapeDrawable getRoundedRectangleBackground(int cornerRadius, int backgroundColor) { + final ShapeDrawable drawable = new ShapeDrawable( + getRoundedRectangleShape(cornerRadius)); + drawable.getPaint().setColor(backgroundColor); + return drawable; + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/SplitBackgroundDrawable.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/SplitBackgroundDrawable.java new file mode 100644 index 00000000000..6c453c86bc1 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/SplitBackgroundDrawable.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin.ui; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +/** + * Provides drawable for a vertically split background. + */ +public class SplitBackgroundDrawable extends Drawable { + private Paint paint; + private int distanceFromTopToSplitPoint = -1; + private int topBackgroundColor; + private static final int DEFAULT_BACKGROUND_COLOR = Color.WHITE; + + public SplitBackgroundDrawable(int distanceFromTop) { + paint = new Paint(); + this.topBackgroundColor = DEFAULT_BACKGROUND_COLOR; + setSplitPointDistanceFromTop(distanceFromTop); + } + + public SplitBackgroundDrawable(int distanceFromTop, int topBackgroundColor) { + paint = new Paint(); + this.topBackgroundColor = topBackgroundColor; + setSplitPointDistanceFromTop(distanceFromTop); + } + + public void setSplitPointDistanceFromTop(int distanceFromTop) { + distanceFromTopToSplitPoint = distanceFromTop; + invalidateSelf(); + } + + @Override + public void draw(@NonNull final Canvas canvas) { + final Rect b = getBounds(); + paint.setColor(this.topBackgroundColor); + float y = distanceFromTopToSplitPoint < b.height() ? distanceFromTopToSplitPoint : b.height(); + + canvas.drawRect(0, 0, b.width(), y, paint); + paint.setColor(DEFAULT_BACKGROUND_COLOR); + canvas.drawRect(0, y, b.width(), b.height(), paint); + } + + @Override + public void setAlpha(final int alpha) { + } + + @Override + public void setColorFilter(@Nullable final ColorFilter colorFilter) { + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButton.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButton.java new file mode 100644 index 00000000000..e0aa1327413 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButton.java @@ -0,0 +1,316 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin.ui.buttons; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; +import android.support.annotation.Nullable; +import android.text.TextPaint; +import android.text.method.TransformationMethod; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.amazonaws.mobile.auth.core.R; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.getRoundedRectangleBackground; + +/** + * Base class for Sign in Buttons + */ +public class SignInButton extends LinearLayout { + /** Image left margin. */ + private static final int IMAGE_LEFT_MARGIN = dp(8); + + /** Image right margin. */ + private static final int IMAGE_RIGHT_MARGIN = dp(8); + + /** Text left margin. */ + private static final int TEXT_LEFT_MARGIN = dp(2); + + /** Text right margin. */ + private static final int TEXT_RIGHT_MARGIN = dp(8); + + /** Min text size in SP. */ + private static final float MIN_TEXT_SIZE_SP = 8; + + /** Max text size in pixels. */ + private static final float MAX_TEXT_SIZE_PX = dp(50); + + /** Color for the border. */ + private static final int BORDER_COLOR = 0xFF000000; + + /** Button Attributes. */ + private final SignInButtonAttributes attributes; + + /** Image View for displaying the Icon. */ + protected ImageView imageView; + + /** Text View for displaying the text */ + protected TextView textView; + + /** Bitmap for the icon. */ + protected Bitmap bitmap; + + /** Boolean to keep track of whether the button should only display the image and no text. */ + protected boolean isSmallStyle = false; + + public SignInButton(final Context context, @Nullable final AttributeSet attrs, + final int defStyleAttr, final SignInButtonAttributes buttonAttributes) { + super(context, attrs, defStyleAttr); + this.attributes = buttonAttributes; + setFocusable(true); + setClickable(true); + this.setOrientation(HORIZONTAL); + this.setGravity(Gravity.CENTER_VERTICAL); + this.setBackgroundDrawable(getBackgroundStatesDrawable()); + + imageView = new ImageView(context); + bitmap = BitmapFactory.decodeResource(getResources(), attributes.getImageIconResourceId()); + final BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap); + imageView.setImageDrawable(bitmapDrawable); + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + imageView.setAdjustViewBounds(true); + + final LinearLayout.LayoutParams imageLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + imageLayoutParams.setMargins(IMAGE_LEFT_MARGIN, 0, IMAGE_RIGHT_MARGIN, 0); + + imageLayoutParams.weight = 0; + this.addView(imageView, imageLayoutParams); + + textView = new TextView(context); + textView.setTextColor(attributes.getTextColor()); + textView.setTypeface(null, Typeface.BOLD); + textView.setSingleLine(true); + textView.setGravity(Gravity.CENTER_VERTICAL); // Gravity.CENTER_HORIZONTAL + final String buttonText; + if (attrs != null) { + // Get styled attributes for the button style and button text. + final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.SignInButton); + if (styledAttributes.getInt(R.styleable.SignInButton_button_style, 0) > 0) { + isSmallStyle = true; + } + buttonText = styledAttributes.getString(R.styleable.SignInButton_text); + } else { + buttonText = null; + } + + if (buttonText != null) { + textView.setText(buttonText); + } else { + textView.setText(attributes.getDefaultTextResourceId()); + } + + final LinearLayout.LayoutParams textViewLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + textViewLayoutParams.setMargins(dp(TEXT_LEFT_MARGIN), 0, dp(TEXT_RIGHT_MARGIN), 0); + + // Use layout weight so the text view will take up the available space in the view. + textViewLayoutParams.weight = 1; + this.addView(textView, textViewLayoutParams); + + updateStyle(); + invalidate(); + } + + /** + * Create the button background. + * @param buttonFaceColor the color for the button. + * @return the background drawable. + */ + private Drawable getButtonBackground(final int buttonFaceColor) { + final int cornerRadius = attributes.getCornerRadius(); + // Set Button shape and background color. + final ShapeDrawable insetBackgroundDrawable = + getRoundedRectangleBackground(cornerRadius, buttonFaceColor); + + // Top Shadow for Button. + final GradientDrawable outerShadowTopDrawable = new GradientDrawable( + GradientDrawable.Orientation.LEFT_RIGHT, + new int[]{attributes.getTopShadowColor(), attributes.getTopShadowColor()}); + outerShadowTopDrawable.setCornerRadius(dp(cornerRadius)); + + // Bottom Shadow for Button. + final GradientDrawable outerShadowBottomDrawable = new GradientDrawable( + GradientDrawable.Orientation.LEFT_RIGHT, new int[]{ + attributes.getBottomShadowColor(), attributes.getBottomShadowColor()}); + outerShadowBottomDrawable.setCornerRadius(dp(cornerRadius)); + + final GradientDrawable border = new GradientDrawable(); + border.setColor(BORDER_COLOR); + border.setCornerRadius(dp(cornerRadius)); + + final LayerDrawable layerDrawable = new LayerDrawable( + new Drawable[] {border, + outerShadowTopDrawable, + outerShadowBottomDrawable, + insetBackgroundDrawable}); + + // Border for the button. + layerDrawable.setLayerInset(0, 0, 0, 0, 0); + + // Top shadow is the furthest down drawable, so it is ok if this overlaps the bottom shadow. + layerDrawable.setLayerInset(1, 0, 0, 0, 0); + + // Bottom shadow does not overlap the top shadow. + layerDrawable.setLayerInset(2, attributes.getTopShadowThickness(), attributes.getTopShadowThickness(), 0, 0); + + // Background must not overlap either of the shadows. + layerDrawable.setLayerInset(3, attributes.getTopShadowThickness(), attributes.getTopShadowThickness(), + attributes.getBottomShadowThickness(), attributes.getBottomShadowThickness()); + + return layerDrawable; + } + + /** + * @return the button background drawable states for when pressed and not pressed. + */ + private Drawable getBackgroundStatesDrawable() { + final StateListDrawable states = new StateListDrawable(); + states.addState(new int[] {android.R.attr.state_pressed}, + getButtonBackground(attributes.getBackgroundColorPressed())); + states.addState(new int[] {}, + getButtonBackground(attributes.getBackgroundColor())); + return states; + } + + private void updateStyle() { + if (isSmallStyle) { + textView.setVisibility(GONE); + this.setGravity(Gravity.CENTER); + } else { + textView.setVisibility(VISIBLE); + this.setGravity(Gravity.CENTER_VERTICAL); + } + } + + /** + * Sets the button style to small, where only the icon will be shown. + * @param shouldSetStyleSmall true if style should be small, otherwise false. + */ + public void setSmallStyle(final boolean shouldSetStyleSmall) { + isSmallStyle = shouldSetStyleSmall; + updateStyle(); + } + + /** + * Set the button text. + * @param text the text string. + */ + public void setButtonText(final String text) { + textView.setText(text); + resizeButtonText(); + } + + /** + * Set the button text from a resource. + * @param resId the resource id containing a string. + */ + public void setButtonText(final int resId) { + textView.setText(resId); + resizeButtonText(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams(); + int sideSize = (int)(getMeasuredHeight() * 0.72); + if (sideSize > bitmap.getHeight()) { + sideSize = bitmap.getHeight(); + } + + // Set image to a square based on the desired height. + layoutParams.height = sideSize; + layoutParams.width = sideSize; + } + + private boolean doesTextViewFit(final float suggestedSize, final RectF availableRect) { + final TextPaint textPaint = new TextPaint(textView.getPaint()); + textPaint.setTextSize(suggestedSize); + final TransformationMethod transformMethod = textView.getTransformationMethod(); + final String text = (transformMethod == null) ? textView.getText().toString() + : transformMethod.getTransformation(textView.getText(), textView).toString(); + + final RectF textRect = new RectF(0, 0, textPaint.measureText(text), textPaint.getFontSpacing()); + + // Return true if the text view fits, even though it may have extra space. + return availableRect.contains(textRect); + } + + private float findBestSize(final float start, final float end, final RectF availableSpace) { + float low = start; + float high = end; + float midpoint; + float bestFit = low; + // Binary search to find the best size. + while (low <= high) { + midpoint = (low + high) / 2; + if (doesTextViewFit(midpoint, availableSpace)) { + bestFit = midpoint; + low = midpoint + 0.5f; + } else { + high = midpoint - 0.5f; + } + } + return bestFit; + } + + /** + * Resize the text to the best fit. + */ + private void resizeButtonText() { + if (getMeasuredWidth() == 0 || isSmallStyle) { + return; + } + + final float minTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, MIN_TEXT_SIZE_SP, + getResources().getDisplayMetrics()); + final RectF availableSpaceRect = new RectF(); + availableSpaceRect.right = textView.getMeasuredWidth() - textView.getCompoundPaddingLeft() + - textView.getCompoundPaddingRight(); + availableSpaceRect.bottom = textView.getMeasuredHeight() - textView.getCompoundPaddingBottom() + - textView.getCompoundPaddingTop(); + + textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, + findBestSize(minTextSize, MAX_TEXT_SIZE_PX, availableSpaceRect)); + } + + @Override + protected void onSizeChanged(final int width, final int height, final int oldwidth, final int oldheight) { + super.onSizeChanged(width, height, oldwidth, oldheight); + if (width != oldwidth || height != oldheight) + resizeButtonText(); + } +} diff --git a/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButtonAttributes.java b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButtonAttributes.java new file mode 100644 index 00000000000..d193d0e60c8 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/java/com/amazonaws/mobile/auth/core/signin/ui/buttons/SignInButtonAttributes.java @@ -0,0 +1,124 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.core.signin.ui.buttons; + +/** + * Sign-in Button Attributes + */ +public class SignInButtonAttributes { + private int cornerRadius; + private int backgroundColor; + private int backgroundColorPressed; + private int topShadowColor; + private int bottomShadowColor; + private int topShadowThickness; + private int bottomShadowThickness; + private int textColor; + private int defaultTextResourceId; + private int imageIconResourceId; + + public int getCornerRadius() { + return cornerRadius; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public int getBackgroundColorPressed() { + return backgroundColorPressed; + } + + public int getTopShadowColor() { + return topShadowColor; + } + + public int getBottomShadowColor() { + return bottomShadowColor; + } + + public int getTopShadowThickness() { + return topShadowThickness; + } + + public int getBottomShadowThickness() { + return bottomShadowThickness; + } + + public int getTextColor() { + return textColor; + } + + public int getDefaultTextResourceId() { + return defaultTextResourceId; + } + + public int getImageIconResourceId() { + return imageIconResourceId; + } + + public SignInButtonAttributes withCornerRadius(int cornerRadius) { + this.cornerRadius = cornerRadius; + return this; + } + + public SignInButtonAttributes withBackgroundColor(int backgroundColor) { + this.backgroundColor = backgroundColor; + return this; + } + + public SignInButtonAttributes withBackgroundColorPressed(int backgroundColorPressed) { + this.backgroundColorPressed = backgroundColorPressed; + return this; + } + + public SignInButtonAttributes withTopShadowColor(int topShadowColor) { + this.topShadowColor = topShadowColor; + return this; + } + + public SignInButtonAttributes withBottomShadowColor(int bottomShadowColor) { + this.bottomShadowColor = bottomShadowColor; + return this; + } + + public SignInButtonAttributes withTopShadowThickness(int topShadowThickness) { + this.topShadowThickness = topShadowThickness; + return this; + } + + public SignInButtonAttributes withBottomShadowThickness(int bottomShadowThickness) { + this.bottomShadowThickness = bottomShadowThickness; + return this; + } + + public SignInButtonAttributes withTextColor(int textColor) { + this.textColor = textColor; + return this; + } + + public SignInButtonAttributes withDefaultTextResourceId(int defaultTextResourceId) { + this.defaultTextResourceId = defaultTextResourceId; + return this; + } + + public SignInButtonAttributes withImageIconResourceId(int imageIconResourceId) { + this.imageIconResourceId = imageIconResourceId; + return this; + } +} diff --git a/aws-android-sdk-auth-core/src/main/res/values/attrs.xml b/aws-android-sdk-auth-core/src/main/res/values/attrs.xml new file mode 100644 index 00000000000..9587cca4043 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/res/values/attrs.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/aws-android-sdk-auth-core/src/main/res/values/strings.xml b/aws-android-sdk-auth-core/src/main/res/values/strings.xml new file mode 100644 index 00000000000..428e7be3bb9 --- /dev/null +++ b/aws-android-sdk-auth-core/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + "Sign-in with %s canceled." + Sign-In Error + Sign-in with %1$s failed.\n%2$s + diff --git a/aws-android-sdk-auth-facebook/pom.xml b/aws-android-sdk-auth-facebook/pom.xml new file mode 100644 index 00000000000..036648f7d9d --- /dev/null +++ b/aws-android-sdk-auth-facebook/pom.xml @@ -0,0 +1,99 @@ + + 4.0.0 + com.amazonaws + aws-android-sdk-auth-facebook + aar + AWS SDK for Android - AWS Facebook SignIn + The AWS Android SDK for AWS Facebook SignIn that holds the client classes used for enabling communication with Facebook SignIn + http://aws.amazon.com/sdkforandroid + + + + UTF-8 + + + UTF-8 + + + + + com.amazonaws + aws-android-sdk-pom + 2.6.0 + + + + + android-support + file://${env.ANDROID_HOME}/extras/android/m2repository/ + + + + + + com.amazonaws + aws-android-sdk-auth-core + false + 2.6.0 + aar + + + com.google.android + android + 4.1.1.4 + provided + + + + + com.facebook.android + facebook-android-sdk + 4.1.0 + false + aar + + + com.android.support + support-v4 + + + + + + + com.android.support + support-v4 + 21.0.0 + aar + false + + + + + + + com.simpligility.maven.plugins + android-maven-plugin + 4.5.0 + true + + + 25 + 19.1.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + diff --git a/aws-android-sdk-auth-facebook/src/main/AndroidManifest.xml b/aws-android-sdk-auth-facebook/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..29bf920341a --- /dev/null +++ b/aws-android-sdk-auth-facebook/src/main/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookButton.java b/aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookButton.java new file mode 100644 index 00000000000..8049a73ee12 --- /dev/null +++ b/aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookButton.java @@ -0,0 +1,103 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.facebook; + +import android.content.Context; +import android.graphics.Color; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Log; + +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.ui.buttons.SignInButton; +import com.amazonaws.mobile.auth.core.signin.ui.buttons.SignInButtonAttributes; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; + +/** + * A Facebook button that will render appropriately for different sizes. By default, the button + * will display showing an icon and text. The button can be set to small style to show only + * the icon. + */ +public class FacebookButton extends SignInButton { + + /** Log tag. */ + private static final String LOG_TAG = FacebookButton.class.getSimpleName(); + + /** Button corner radius. */ + private static final int CORNER_RADIUS = dp(4); + + /** Button background color. */ + private static final int FB_BACKGROUND_COLOR = 0xFF3C5C95; + + /** Button background color when pressed. */ + private static final int FB_BACKGROUND_COLOR_PRESSED = 0xFF2D4570; + + /** + * Constructor. + * @param context context. + */ + public FacebookButton(@NonNull final Context context) { + this(context, null); + } + + /** + * Constructor. + * @param context context. + * @param attrs attribute set. + */ + public FacebookButton(@NonNull final Context context, + @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructor. + * @param context context. + * @param attrs attribute set. + * @param defStyleAttr default style attribute. + */ + public FacebookButton(@NonNull final Context context, + @Nullable final AttributeSet attrs, + final int defStyleAttr) { + super(context, attrs, defStyleAttr, + new SignInButtonAttributes() + .withCornerRadius(CORNER_RADIUS) + .withBackgroundColor(FB_BACKGROUND_COLOR) + .withBackgroundColorPressed(FB_BACKGROUND_COLOR_PRESSED) + .withTextColor(Color.WHITE) + .withDefaultTextResourceId(R.string.default_facebook_button_text) + .withImageIconResourceId(R.drawable.facebook_icon) + ); + + + if (isInEditMode()) { + return; + } + + try { + final SignInManager signInManager = SignInManager.getInstance(); + signInManager.initializeSignInButton(FacebookSignInProvider.class, this); + } catch (Exception exception) { + exception.printStackTrace(); + Log.e(LOG_TAG, "Cannot initialize the SignInButton. Please check if IdentityManager : " + + " startUpAuth and setUpToAuthenticate are invoked"); + } + } +} diff --git a/aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookSignInProvider.java b/aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookSignInProvider.java new file mode 100644 index 00000000000..8d273fa6083 --- /dev/null +++ b/aws-android-sdk-auth-facebook/src/main/java/com/amazonaws/mobile/auth/facebook/FacebookSignInProvider.java @@ -0,0 +1,292 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.facebook; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.support.annotation.NonNull; +import android.util.Base64; +import android.util.Log; +import android.view.View; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.mobile.auth.core.signin.SignInProviderResultHandler; +import com.amazonaws.mobile.auth.core.signin.SignInProvider; +import com.amazonaws.mobile.auth.core.internal.util.ThreadUtils; + +import com.facebook.AccessToken; +import com.facebook.AccessTokenTracker; +import com.facebook.CallbackManager; +import com.facebook.FacebookCallback; +import com.facebook.FacebookException; +import com.facebook.FacebookSdk; +import com.facebook.login.LoginManager; +import com.facebook.login.LoginResult; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.Locale; + +/** + * Sign-in provider for Facebook. + */ +public class FacebookSignInProvider implements SignInProvider { + + /** Log tag. */ + private static final String LOG_TAG = FacebookSignInProvider.class.getSimpleName(); + + /** The Cognito login key for Facebook to be used in the Cognito login Map. */ + public static final String COGNITO_LOGIN_KEY_FACEBOOK = "graph.facebook.com"; + + /** Timeout for refreshing the Facebook Token. */ + private static final long REFRESH_TOKEN_TIMEOUT_SECONDS = 15; + + /** Facebook's callback manager. */ + private CallbackManager facebookCallbackManager; + + /** Latch to ensure Facebook SDK is initialized before attempting to read the authorization token. */ + private final CountDownLatch initializedLatch = new CountDownLatch(1); + + /** AWS Configuration Object. */ + private AWSConfiguration awsConfiguration = null; + + /** list to store permissions. **/ + private static ArrayList permissions = new ArrayList(); + + /** + * Intitializes the SDK and debug logs the app KeyHash that must be set up with + * the facebook backend to allow login from the app. + * + * @param context the context. + * @param configuration the AWS Configuration. + */ + @Override + public void initialize(@NonNull final Context context, + final AWSConfiguration awsConfig) { + this.awsConfiguration = awsConfig; + if (!FacebookSdk.isInitialized()) { + Log.d(LOG_TAG, "Initializing Facebook SDK..."); + FacebookSdk.sdkInitialize(context); + } + initializedLatch.countDown(); + Log.d(LOG_TAG, "Facebook SDK initialization completed"); + } + + /** + * Get the SignedIn Access token. + * @return the Facebook AccessToken when signed-in with a non-expired token. + */ + private AccessToken getSignedInToken() { + try { + initializedLatch.await(); + } catch (final InterruptedException ex) { + Log.d(LOG_TAG, "Unexpected interrupt."); + } + final AccessToken accessToken = AccessToken.getCurrentAccessToken(); + if (accessToken != null && !accessToken.isExpired()) { + Log.d(LOG_TAG, "Facebook Access Token is OK. Token hashcode = " + accessToken.hashCode()); + return accessToken; + } + + Log.d(LOG_TAG, "Facebook Access Token is null or expired."); + return null; + } + + /** {@inheritDoc} */ + @Override + public boolean isRequestCodeOurs(final int requestCode) { + return FacebookSdk.isFacebookRequestCode(requestCode); + } + + /** {@inheritDoc} */ + @Override + public void handleActivityResult(final int requestCode, + final int resultCode, + final Intent data) { + facebookCallbackManager.onActivityResult(requestCode, resultCode, data); + } + + /** {@inheritDoc} */ + @Override + public View.OnClickListener initializeSignInButton(final Activity signInActivity, + final View buttonView, + final SignInProviderResultHandler resultsHandler) { + if (buttonView == null) { + throw new IllegalArgumentException("Facebook login button view not passed in."); + } + + facebookCallbackManager = CallbackManager.Factory.create(); + + LoginManager.getInstance().registerCallback(facebookCallbackManager, new FacebookCallback() { + @Override + public void onSuccess(final LoginResult loginResult) { + Log.d(LOG_TAG, "Facebook provider sign-in succeeded."); + resultsHandler.onSuccess(FacebookSignInProvider.this); + } + + @Override + public void onCancel() { + Log.d(LOG_TAG, "Facebook provider sign-in canceled."); + resultsHandler.onCancel(FacebookSignInProvider.this); + } + + @Override + public void onError(final FacebookException exception) { + Log.e(LOG_TAG, "Facebook provider sign-in error: " + exception.getMessage()); + resultsHandler.onError(FacebookSignInProvider.this, exception); + } + }); + + final View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(final View view) { + LoginManager.getInstance().logInWithReadPermissions(signInActivity, + FacebookSignInProvider.permissions); + } + }; + + buttonView.setOnClickListener(listener); + return listener; + } + + /** + * Add the login permisisons needed by the application. + * The input has to be a string or array of strings, where each + * string represents a permisison. + * + * Eg: + * FacebookSignInProvider.setPermissions("public_profile"); + * FacebookSignInProvider.setPermissions("publi_profile", "email"); + * + * @param userPermissions The list of permissions required + */ + public static void setPermissions(final String... userPermissions) { + synchronized (FacebookSignInProvider.permissions) { + for (String permission : userPermissions) { + FacebookSignInProvider.permissions.add(permission); + } + } + } + + /** {@inheritDoc} */ + @Override + public String getDisplayName() { + return "Facebook"; + } + + /** {@inheritDoc} */ + @Override + public String getCognitoLoginKey() { + return COGNITO_LOGIN_KEY_FACEBOOK; + } + + /** {@inheritDoc} */ + @Override + public boolean refreshUserSignInState() { + return getSignedInToken() != null; + } + + /** {@inheritDoc} */ + @Override + public String getToken() { + AccessToken accessToken = getSignedInToken(); + if (accessToken != null) { + return accessToken.getToken(); + } + return null; + } + + /** {@inheritDoc} */ + @Override + public String refreshToken() { + + AccessToken accessToken = getSignedInToken(); + // getSignedInToken() returns null if token is expired. + if (accessToken != null) { + return accessToken.getToken(); + } + + Log.i(LOG_TAG, "Facebook provider refreshing token..."); + final CountDownLatch countDownLatch = new CountDownLatch(1); + + // The constructor of the AccessTokenTracker creates a broadcast receiver that keeps this class + // alive until a broadcast is received as a result of calling refreshCurrentAccessTokenAsync() below. + final AccessTokenTracker tokenTracker = new AccessTokenTracker() { + @Override + protected void onCurrentAccessTokenChanged(final AccessToken oldAccessToken, + final AccessToken currentAccessToken) { + this.stopTracking(); + if (currentAccessToken == null) { + // We cannot refresh the token. + // The user may have revoked permissions by going to his settings and deleting your app. + // This will cause the call to fail, and the app will likely want to send the user + // back to the sign-in page. + Log.d(LOG_TAG, "Facebook token can't be refreshed, perhaps the user revoked permissions."); + } else { + Log.i(LOG_TAG, "Facebook provider token has been updated."); + } + countDownLatch.countDown(); + } + }; + + try { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + // Refreshes access token in the background and wakes up the AccessTokenTracker + // to receive the result. + AccessToken.refreshCurrentAccessTokenAsync(); + } + }); + + try { + Log.d(LOG_TAG, "Facebook provider is waiting for token update..."); + if (!countDownLatch.await(REFRESH_TOKEN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + Log.w(LOG_TAG, "Facebook provider timed out refreshing the token."); + return null; + } + } catch (final InterruptedException ex) { + Log.w(LOG_TAG, "Unexpected Interrupt of refreshToken()", ex); + throw new RuntimeException(ex); + } + + accessToken = getSignedInToken(); + if (accessToken == null) { + Log.w(LOG_TAG, "Facebook provider could not refresh the token."); + return null; + } + } finally { + tokenTracker.stopTracking(); + } + + return accessToken.getToken(); + } + + /** {@inheritDoc} */ + @Override + public void signOut() { + Log.d(LOG_TAG, "Facebook provider signing out..."); + LoginManager.getInstance().logOut(); + } +} diff --git a/aws-android-sdk-auth-facebook/src/main/res/drawable-hdpi/facebook_icon.png b/aws-android-sdk-auth-facebook/src/main/res/drawable-hdpi/facebook_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a414e151e6194212647e1132d438f4123d94108 GIT binary patch literal 1054 zcmV+(1mXLMP)InOyf91gjHQyvEdr9CT+6o`~@ zxed0s9EqLLA~r?|#LA$w+ETQggX7L$LB?m+Aud;9XF)u9S;$y77!P1aZ=#jXU;?A*khvge z+}eg7-VH0DNFKkKx*4IkHklTWx=2voU04N05xtR%*=C zfyC!ZU|O;1^41xbqEH}YywiCE;qczl+BWT*_bU(#=w;Q;e*jWWK-O6h&x2sFnh%a# zv_%%f^kVlQJTkNvmbG9qTWH(UZNI zzx+&j%V!|+{@DvBh35%hLf_DWZ$07-kgEh_M!D&`6vbO0*xv@($M`JRJq_a6{$d_> zI`j1jNF)hbPkJQzp3LRo!TYn;;2b2}X`=QOF}MFxb_0Z-Gi%{*oZE;YHb(*vh5caM z_wjKM!cgZ?yUtow?;J0PeD}H5{L&U_yYJkV^hF23=oDpz&9zt%7K8<1L0AwLgau(i z7z*N}f&p)V92X2oY#gkz@kI%TH$a?9IIvU*i)z#w?-teIz9Rjcv){$QkE|Njd;5>{?pWh$a zh_)gGH&ZMRE@^kvY_$hqsSvsd3sf+8JgxY=MmsUsPC#hI<=5F`bN;a}5))`Edcpzm Y3odK3kBPD*l>h($07*qoM6N<$f(wQ2DF6Tf literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-facebook/src/main/res/drawable-ldpi/facebook_icon.png b/aws-android-sdk-auth-facebook/src/main/res/drawable-ldpi/facebook_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e7439ad185a401e8c8ed32360138f0a4cd5082ba GIT binary patch literal 699 zcmV;s0!00ZP)P000>X1^@s6#OZ}&0007lNklCyvp~j0X^hZ2UJyGfjC>VRNHU(NuqatXmHPHjc zYOoTUkRVb@YNeC{g$0)H`sNi-qKz(~CpVeAi0s%I^?1`EEJeqA3w{O@P5GVuMXyhrCYOV56T)oK)fl{j% zQj|(aWkfnL!qEi6n+Zf>yU2-!DrCgukp;TLdLa3N1fM?wgZUXQUAvEdoeO-gZRqHkM*BG{+SFEbuyy5i2!*257$ly^Ks)S2>sd?H zst^WT@ihpck6*5S;wNtDw(!6wFWkn2Wf=~)AB)~KM7NS$0Chved=U#Qkcyp{GN5(P zu!OC6n)}Eq3Tp`BbiZf8Z5-06vEe$)Vn6$H48@R24UjzPT2#Gd2wQJhpCip;{Hjj= z7(yRLVo8KHb`bph6?TV@2d_Xzr@UAXZF4_aRxB!q?548N4mtTx{!vkF>VuU8P_a`3 zGw^?m@j_Ju*-Pi3x$59STe{6 zVHwgs`7e&&{k$dr|)SX~6l zu^&oTH^)F#39}3g=4B+3S?(9}QvDJ>v`)$r&NQP={zDER#(w~zsp4-!^aRl=oK{Gx z^AD*(PM5o|9TQB#+dG*~7Mdz3wcpv;lvnQ8yJXf{WY){LogTa+kKcS! h^B!eDfYvVCv2Q1vSTduUq*DL@002ovPDHLkV1htZLp}fi literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-facebook/src/main/res/drawable-mdpi/facebook_icon.png b/aws-android-sdk-auth-facebook/src/main/res/drawable-mdpi/facebook_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..712a7a374e36ccb09fa4e24a95192f83bcba125c GIT binary patch literal 919 zcmV;I18Dq-P)3j(ZOLGNL2oYpWBTV`sMBU~T4J}=!=87^R&9c&{ zIV-1u8mM8un4iV)-`02%G6a7$^aeMhzG?@W$`7H z8Y0_>2gS`T(0e6RBe5Ztmw^SR3)XoDez3dcnp=R?=77f&3UTU6tGEyp+&b^X$e0-q zI|flw`x@7^ohW9yQS}n4`fkjafAVz^gP8hmgGToj3E8(0E!SZ8F)jA2C_Y1rm{bi+ zLsM|OJ&}Q!wb)U_?s#uzC1TSwh)b(LT>7f$*sc^cOvBS~dzQtAa4x#h*!BsDxwSrk zpx^{U$D&i!kS43p&odDLh&h`RSIay2ol@ZV3?IP$tZJm^-9>i(edOe~;KZe7d>J>h z%_%Yj-POm>lTZ}s5fhY!Xt^57(r0*O7{%AgIn2!3Ff}_5m)j$LcMycZG(i_bY@q(^ znQg>3#!+4>e>be<6QV)%hCz^Me;5ZQ0-G@65EP6#?`kHP4l3%}-8dYifCC z6jgMhy7@g{L(g@3BP`6y1w+tA(>?lt?TIDWakLy#nToZhDx@>~qL!7)%CU1rNoQ_j z{F_y{Z7(jl(e$LB0hIZ=AboB5y&~|QDX;i3|Lzc+i2%frvftb>Jg|S05-dSnQ$I)W zZx=!3Xkg{omT(hMhf6sOrJTN;2*M9SfvHqg+31`OrU4 zudZRL;x`_q_YEs=W-r%O))@ tHhp=IUu^O!lxuv3#i0yvfWpEpzW{cGqcs!4l8*oY002ovPDHLkV1hVNv#S6A literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-facebook/src/main/res/drawable-tvdpi/facebook_icon.png b/aws-android-sdk-auth-facebook/src/main/res/drawable-tvdpi/facebook_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..df29c57ee45c82d53057e1c7df86d0078c42a6a1 GIT binary patch literal 901 zcmV;01A6?4P)txfu(vgk0y%p~7t=1%*Yd(ZvuIXMaiyOhQH11Y)n`s}NE zdrn!4|8x?Gc(O|kcH+xaYYr-cz*!~wPd)up!Z+)RZ)ScC)+7?~5Et>CDAA8`aQXte zu_3tO^C4+Pe3t%6AtkTQprDYTFQEs?d=VdUQm7~>W@ria12l1}%3J*k3U=#0g3c*# zkq+AJ*@k0%3wCF;oEt89*z=@;7PdUFEjnT8n?%{2=g6xzF`1D|sxl-0x*65?dNDBa zBMe*;Xz#WU*10v*w!KH@B^}bvYoIJ@jHzSAI<)nUV$0=~4tkpbk50@%eX9#cFKTf( zzX3-I0!l3m7u};VtvwP$+m_ealD|MYo82I6JV_3{DR}VM@D3Sj?XG}1v|3{51vbm7 z@|L)esfCS5zn}?-t;X@o&1moaC=ImRB}*E z`D){vch8{(mj}A;VdfUvn6rE8nh_(PXR+oG_*k0RphAK799rA8Gj!3-4y@XRK zbZ2bnv?2}e*7viG>fS%lN(TKvH^ALRh87$8q14c{e8c{LL{P@1+ z4dxdeQSXsFUXimr0`7i2j&qeJu%aSFM()wUoFY=0?6}Thwio$M!3{Hp$7jR9MS*T( z2SWg`F+y=p!oSuRDOE))CZ=sX(b^N}-R2iNNy(sdt4#Q1bHFEdcq9^<3WMd0!1Hx0)3>4j*Yjs%uZL^)xSp=D?pT85UP{~u^EdoE%?^PUTJi^S?~B^sJI zPnH_(qV^F9sHtNpF7$5yH_%6%5>oOS3<~OiQYX|B(y>jHrhhKsQS$)Ln`0&WHMM>C zKC{Bc+T;EXaS>nEmFBUG!Y7A#tC_&56H1{%F+;(gD6wkB&noPgVk_WR{o{S&p^%^% b4g&uHS1^@s6$I7^K000F|NklV{GGX0!|#;fC8m&l!MRr=`C+*%SFuS?R{?YrD?gjm-FE~=Q-!xK95ev zkL9QG^J|~ZDU?dGOt!8pv*ck~&NADQP_Rq#WSgX}?EU4Xup9R4A|O)poGCeG=}PWF zhr9S#WAIx?8_;fHM(q6(*XlV*2?K`u1B*1}UZByxXJ~Nr5#1i1q?-f(Q19Id>SM3{?ES#K zhcx)-BN`c>qA_+}yvadzFaTk8-p>uZ*gj0hD_hC%?rF+2o}k?Y7E0S^rL+Rb!nG?u z{MqxboFwy+8oJgyPJv)Z{V;-yAsQZgOpf|)DtNn!^b7#YjW~!*!-@5;vN9rKpEDz= zscn#a%%fBdW5E}o&YR;@e5`>Rm0_@Q10zNQjNY{7b%Xdh$p8cMe{K6OU|2bf^S`SQ24V#gr_#?F6?+AYeeQsrUMhBy@klkj{C>@f3g4@t z{U5mK>K~&NP#X-Nf03$wzCka%dP)(D>_UkS9c`d5Y%NstOD}KKwBHz`j-Ift$wCYY zEvYO6McQnwYFFeD=`6#Ae&9t}l zCpuoy8uL03T7@o_rn~37i3J1cvg_Ag+ATM0dG{)YGK~tGIdQI$F3ZGSOs!j) zP2x?@P;OBL-TP}Q@j6||1gp>W&@+3!r)T&6z#k586dod@ou>o{jEy{Gj(#mGz5dBr zy8pnPcrZ{y-~n=7(=DoR?x*^u^=fDi>+qN76nTZ=ozwg~ow!y*Q}pY#_l$4)U9}yn zO#lSLB@jc%2Y78_P+ps$)`@nlNP}2ACA^xeR?*K8Fa!(%L%h#6A}gNtCh58k+a_SQ zfJ9gezjYYf>w&`FOMET7!<}i8-ja7ou&?ftFzc1S4fEu8FPLHXhRbf=9kZ_O?Vmm<3)nB`p(!uw}PDcCjvy9K~yur?e)1yS}%>7=YTnUPAKo9rq1 zk-oBDW`E}v@5Sr_aM%ft0EpDQ&r@`350JsO3D_+Fi{ueqlT`?oh!GeXvPbaaUoOC= Ueg{7G*8l(j07*qoM6N<$f}1+2oB#j- literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-facebook/src/main/res/drawable-xxhdpi/facebook_icon.png b/aws-android-sdk-auth-facebook/src/main/res/drawable-xxhdpi/facebook_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0380a7f8c0156fca845cd9c4ac005c2a194819fb GIT binary patch literal 2008 zcmb7_dpr{g8^_nmC1-9k%tCVS_HHT1HO5>=HDY~++=WtdYb(yMxukL(xg3{max26} zUb)mZzxU7gkLP(lzvtiI@ALg-U@kh!%D`j*0D!Eslb!qi z4f#3JlKa~u;q)i~AXVXPXX8Pb5|sUQ*&_rxuw??b7BLeYK8h#;`!TnQ^IWwot2Q+AN-Y zwIcMJMOJiajEJ~-estaUO3e|aU$BDmU^L?uz0HvKC!uQG|3w^c{$?VRq{FMh)lGOW ztEKggs0qEd>Wj>8{+VCgjIA6Cw5T?j_I{+|`20AFYpnO5-)#G2N)m+1yL@(*ib!^r zyGKC=)<=^GpcAPirlO&FELvx>%Sk12JiXs$=66aTtF-E6di2|?p?)a(SSKnZ7|Qg_en@K@P<23MDbKdk@pI_&MS zwF15S2L{>vYcQw~?6?^8dcJ^IBRxA7n6l>TY(|MQs(YwTs!mbqLH5W?cK0UUMy^fL zhHS@eKQeWq78bK+lH7+o9006|5ED~}w# zGnm}doBC?JyYdQeX}Kb7iKu#0{7q`AX-R5IclT50cT3!sFZJdNm;7>^Ye%%pLaUj< z^UK}LywZMS56=6iFc3;DNYcOMMI0w%HFAu9+WyC9z7C{A3}E|}*vC-Rf4l)pv7K8u z%PENkrccazy^LaI8_Twc`d!7P7neq?}YRc$Z$}t%p``02P z`lRaWUf$c>@92Nbbbk=4O=+zc%oBwhafP#-IdG!QgQk9=nh?R)0uCdjG?2 zxM~!hPLD%CtmzG1g+`1A_R)9V*DC-63{O;cOkT*EuWCW)Sv)~N9}EfaciZCZD^JDk z)YU@KPF`=5mgi{MigH>Y`+j(Q$BJVA{dfNVPD13Xw&rQJsjHkx;kEW-Jo?CvWpwLk zL_JcAoWv-gS?aOkC3!ss=I!MtFcdefIgWe<;-m4Ethf-X%j*QihoQ7h4dW*?0zE_zV7Qug|_NYCel0`IOLGFCWd+`u0Hk#Pe>2DKcDG@Q`Og$9@vJ{ z*G0mpLVkRfrw8gl4rv`9RMo%4XpVWIvQ}5FEJI?h5a8+G%v&Zk0 zbMmvbRUVwNrOx#-w+218KY>C>($8@V$hI^N6^g!}+JLVW)9-QZp4i}u@=%@|+4Ok) zZCx9inzOgqZZG3sU7!?#^!m1gx2soc>#E2!R|spMl^4p3^1c+iwWxDg#zEo5a!fei z)F@x!m!-#B>MymkB?1hUXa+|kDJR8I3DR;G3CF2aMzOL3}{kk}q!MB_6&kDlX_`fkt15jXpo<4xE3 zKFRPLJ8{`WIcGikbbDi6he6}X_CKWJVsH5IVYw6^0J#ijn(mU>6h{+d=Qu1=OK-vX z^nMO&v`p^S0&gxuGFA2BKSuqJYyX$%zZeG!@u5v*rfF+ZUBvzAO3zy(&R#QQJ>)74 z{6yD1rh7R0HsPVqhZ2(xME=~Ng!H{RfTnp@e|bQQ>Afg*@Z;UH`9f6k>XK3EAvsAV z<7%kWNGZ+4!mG(o-fuf|b=TjJO9sB;T8O2jy6o?Iw)uZpadvnpC_3gFH9sAD3v1$N zS>%)qtoS3-PE@Wcg6D^#hl=Y_Bad2P{k@YVV93$ekV4}@{Zzb0p2AbI6S%yi(;c~u z02o?~zU2r=I}t*qQK`f+TR>m1TY_+}wTP`hbKAA-*WZRlpf}WsfWR;`VL)MGZ+_pLUgkN806ZcqLx#oNo;xYFmrI@vJ9Y`6ra`FrrxB=@sF_D{vL{XRh-kk8 hffHZHEZ9SQFaW3Vtyk-N*M4IIob4~#HQM@<{syy8%tHVG literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-facebook/src/main/res/drawable-xxxhdpi/facebook_icon.png b/aws-android-sdk-auth-facebook/src/main/res/drawable-xxxhdpi/facebook_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9f44cc94c13b4c21ca249a6fc3772beb60e34121 GIT binary patch literal 2484 zcmdUx`#%$kAI3K;(`2jW-f&#%Wg8UCh&7uD-fJBU|BcQGiu>b(>RXu@!+=t^ClEcMLS{>bM=0bLSi8j%;^xu?qF?gy&y{bGbE$>w@(J3v3uL9B zcRqKIbw*?1?a|R)*Bng?Mhlt}kbHxm??zvvP0|{fMVmU2mM<;l3Z;G9+k-UPw8eRs zB+T??RMg5zoW>a4&n!N3S+gJlSFDFLI0JOad*QyGQ~sJXR=F=j>2SUMzeHS->=*xOH#(qm@^fRb zBV>1#YlQHv`v>lrw@&hV&uH80!CYf(;NseYc-S_383hJP)Sbr?cB?lH&kf#(Sz(+~ zn%>=s?@%6XtS$+79$8krRe{Go7VT_j42ph&BKqt)-wtRK4Fg4AHD!bFW?s*N%HI2+ zUX6N(#i2;jBDS z)0a`FIR0q7m{y(3w7+g^dVqUT7vKGD-&e{UKR(OW`$&F9NgygKoFypmo;W`?Y0ZwBWR(`_3Fj zU=+G@V#`A9K@vLL-JIp$8g^Gw@+U$2VmEdl-JU8()~6mF^(KS%%=iHaGkNl009gHP z4M6bLPzefvn*ZSoWaap3B3Se~LmU~8*RnnGjH+w%0PSlP<$E$4%Q>gYUf*}e5Z!Uk zuLdvhe40Key5MN0;#J(>1V)4#C%?LZh5d4EMW-^Fgz0@1oHDe&UJ7C_^hEgVJ^A$H zz%vaM%j@qgUmHaBEgKwW)TUj*VnY2=jt3|ZzCSbquAKiV=!2L$o6XcKHjACQU{m@i zBW+LvuQ5P$^t7p40wYc+l`V0(CehN$J0C||il8Ezl;-$bFI<(Bg_=?(Mx6h8Qi!`lSvvAj0L0NRk>|6+FpT^?5>OU1Rx_ z#&|9@``{{Dm=xv2TEl)Q64D?Lgk^4x^8y_*czqBgL|7l0VtX&oz5dM$05qE*)ERtq zd`0}wR&xwMan-S9K)HsSlKXLJa(l)2iWc1d)m-C+1zfxEPuJBCHYB@Q5%uuiZ0={p z+9#xcOzZ}q@}weFZ?zs;U&`>kzLcV$@gL=204VI%TEa{5RaoNNz~l!@B8u-njORC1 z7qXxD8C*z~sSpx{#Aaw%fc20{2y$j0x&1fRCg^9>jFD+~ zmupNJ|N9r-6ohitsGn^zV?jN&4tBaV-E3{DtSCPYLZTeHQj1~wld;|Y;ePwi6#by{ z+GXsDQuy%<5E1E8qw|2Eu2 zhBNyP*#WZ*DHodsnjFWye3Hg0NZGlJD=ZR%e2!XIVw9*5V zy+p1mbHWsB8!pvPXp~WAYo9EofaXU{B)4YRV;DAqU9&yEx4qK|M8$|dHJ*FqdSF0${SFFpbj@@m$O4_~fQ z?tS`5>N^Ip&+10M$$e*hD*lb=)W;q$;>+Kr$Z~U2L&Ofjp;1WEP5=ev-QnC33WVsF z%_%qyyVt?(I0lL#=xPw6YUaHGCrGPp{*X10n(uCPbbfHnnmt>1+;09-kqqE^=!I8E zRruuHI7I+wMylRLYh&;Mh_caRf93~J%(L6+06U6Lx%0>|-FvYOh@9es-~Q?}o>w~^ zvOF$s+LjhFUcoyy>=@E@4+`JtU>@_r z$0YhU*-p6FDpnLr+uX047E(|$~H~0``|GCsyZr;cllg!8l+_tZ)$>} zkIXkzyyX8kxw{rhd_%Z=)FxRN*gV$3*sT(=HT0P|+lI;LkpJ;Ln$Wa`S?eDD08)d?x!%(9~YUp9f3`cf5%jN6_lKz-jrLG)DG)x4q$pg#80TM&gV0b zuS2}S)VyL+tOY>&Ypq+%5%uMS{x|2nY-ym`o2f{GrS;^-JPg yGXcqeFQ#cbW~YB;Rcmtu9RInyrc90 literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-facebook/src/main/res/values/strings.xml b/aws-android-sdk-auth-facebook/src/main/res/values/strings.xml new file mode 100644 index 00000000000..6878a786479 --- /dev/null +++ b/aws-android-sdk-auth-facebook/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Continue with Facebook + diff --git a/aws-android-sdk-auth-google/pom.xml b/aws-android-sdk-auth-google/pom.xml new file mode 100644 index 00000000000..b543280a98c --- /dev/null +++ b/aws-android-sdk-auth-google/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + com.amazonaws + aws-android-sdk-auth-google + aar + AWS SDK for Android - AWS Google SignIn + The AWS Android SDK for AWS Google SignIn that holds the client classes that are used for enabling communication with Google SignIn + http://aws.amazon.com/sdkforandroid + + + + UTF-8 + + + UTF-8 + + + + + com.amazonaws + aws-android-sdk-pom + 2.6.0 + + + + + android-support + file://${env.ANDROID_HOME}/extras/android/m2repository/ + + + google-android-gms + file://${env.ANDROID_HOME}/extras/google/m2repository/ + + + + + + com.amazonaws + aws-android-sdk-auth-core + false + 2.6.0 + aar + + + + com.google.android + android + 4.1.1.4 + provided + + + + com.android.support + support-v4 + 24.2.0 + false + aar + + + + com.google.android.gms + play-services-auth + 9.8.0 + false + aar + + + + + + + com.simpligility.maven.plugins + android-maven-plugin + 4.5.0 + true + + + 25 + 19.1.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + diff --git a/aws-android-sdk-auth-google/src/main/AndroidManifest.xml b/aws-android-sdk-auth-google/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..1950a7dc0e7 --- /dev/null +++ b/aws-android-sdk-auth-google/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleButton.java b/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleButton.java new file mode 100644 index 00000000000..71e45733682 --- /dev/null +++ b/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleButton.java @@ -0,0 +1,113 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.google; + +import android.content.Context; +import android.graphics.Color; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Log; + +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.ui.buttons.SignInButton; +import com.amazonaws.mobile.auth.core.signin.ui.buttons.SignInButtonAttributes; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; + +/** + * Represents the Google SignInButton. + */ +public class GoogleButton extends SignInButton { + + /** Log tag. */ + private static final String LOG_TAG = GoogleButton.class.getSimpleName(); + + /** Button corner radius. */ + private static final int CORNER_RADIUS = dp(4); + + /** Button background color. */ + private static final int GOOGLE_BACKGROUND_COLOR = Color.WHITE; + + /** Button background color when pressed. */ + private static final int GOOGLE_BACKGROUND_COLOR_PRESSED = Color.LTGRAY; + + /** Text Color. */ + private static final int TEXT_COLOR = Color.DKGRAY; + + /** Button top shadow thickness in pixels. */ + private static final int BUTTON_TOP_SHADOW_THICKNESS = (int) dp(3); + + /** Button bottom shadow thickness in pixels. */ + private static final int BUTTON_BOTTOM_SHADOW_THICKNESS = (int) dp(3); + + /** + * Constructor. + * @param context The activity context + */ + public GoogleButton(@NonNull final Context context) { + this(context, null); + } + + /** + * Constructor. + * @param context The activity context + * @param attrs The AttributeSet passed in by the application + */ + public GoogleButton(@NonNull final Context context, + @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructor. + * @param context The activity context + * @param attrs The AttributeSet passed in by the application + * @param defStyleAttr The default style attribute + */ + public GoogleButton(@NonNull final Context context, + @Nullable final AttributeSet attrs, + final int defStyleAttr) { + super(context, attrs, defStyleAttr, + new SignInButtonAttributes() + .withCornerRadius(CORNER_RADIUS) + .withBackgroundColor(GOOGLE_BACKGROUND_COLOR) + .withBackgroundColorPressed(GOOGLE_BACKGROUND_COLOR_PRESSED) + .withTextColor(TEXT_COLOR) + .withDefaultTextResourceId(R.string.default_google_button_text) + .withImageIconResourceId(R.drawable.google_icon) + .withTopShadowThickness(BUTTON_TOP_SHADOW_THICKNESS) + .withBottomShadowThickness(BUTTON_BOTTOM_SHADOW_THICKNESS) + ); + + if (isInEditMode()) { + return; + } + + try { + final SignInManager signInManager = SignInManager.getInstance(); + signInManager.initializeSignInButton(GoogleSignInProvider.class, this); + } catch (Exception exception) { + exception.printStackTrace(); + Log.e(LOG_TAG, "Cannot initialize the SignInButton. Please check if IdentityManager :" + + " startUpAuth and setUpToAuthenticate are invoked"); + } + } + +} + diff --git a/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInException.java b/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInException.java new file mode 100644 index 00000000000..172eac646ca --- /dev/null +++ b/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInException.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.google; + +import com.google.android.gms.auth.api.signin.GoogleSignInResult; + +/** + * Encapsulate exceptions that occurred due to a Google Sign-in failure. + */ +public class GoogleSignInException extends Exception { + + /** Message to express unavailability of SignIn status. */ + private static final String SIGN_IN_STATUS_UNAVAILBLE_MESSAGE = ""; + + /** Reference to the SignIn result. */ + private GoogleSignInResult signInResult; + + /** + * Constructor. + * @param signInResult the GoogleSignInResult. + */ + public GoogleSignInException(final GoogleSignInResult signInResult) { + super(signInResult.getStatus().getStatusMessage() != null + ? signInResult.getStatus().getStatusMessage() : signInResult.getStatus().toString()); + this.signInResult = signInResult; + } + + /** + * Get the reference to SignIn result. + * @return GoogleSignInResult containing error status information. + */ + public GoogleSignInResult getSignInResult() { + return signInResult; + } +} + diff --git a/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInProvider.java b/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInProvider.java new file mode 100644 index 00000000000..3c320a6539e --- /dev/null +++ b/aws-android-sdk-auth-google/src/main/java/com/amazonaws/mobile/auth/google/GoogleSignInProvider.java @@ -0,0 +1,479 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.google; + +import android.Manifest; +import android.accounts.Account; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.View; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.mobile.auth.core.signin.SignInPermissionsHandler; +import com.amazonaws.mobile.auth.core.signin.SignInProviderResultHandler; +import com.amazonaws.mobile.auth.core.signin.SignInProvider; +import com.amazonaws.mobile.auth.core.internal.util.ThreadUtils; + +import com.google.android.gms.auth.GoogleAuthException; +import com.google.android.gms.auth.GoogleAuthUtil; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.OptionalPendingResult; +import com.google.android.gms.common.api.Scope; +import com.google.android.gms.common.api.Status; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * Sign in Provider for Google. + */ +public class GoogleSignInProvider implements SignInProvider, SignInPermissionsHandler { + + /** Log tag. */ + private static final String LOG_TAG = GoogleSignInProvider.class.getSimpleName(); + + /** The Cognito login key for Google+ to be used in the Cognito login Map. */ + public static final String COGNITO_LOGIN_KEY_GOOGLE = "accounts.google.com"; + + /** + * Arbitrary activity request ID. You can handle this in the main activity, + * if you want to take action when a google services result is received. + */ + private static final int REQUEST_GOOGLE_PLAY_SERVICES = 1363; + + /** Request code used to invoke sign in user interactions. */ + private static final int RC_SIGN_IN = 16723; + + /** Permission Request Code (Must be < 256). */ + private static final int GET_ACCOUNTS_PERMISSION_REQUEST_CODE = 93; + + /** Client used to interact with Google APIs. */ + private GoogleApiClient mGoogleApiClient; + + /** Android context. */ + private Context context; + + /** Flag indicating sign-in is in progress. */ + private boolean signingIn = false; + + /** The sign-in results adapter from the SignInManager. */ + private SignInProviderResultHandler resultsHandler; + + /** When signed in, the signed in account, otherwise null. */ + private volatile GoogleSignInAccount signedInAccount = null; + + /** + * The auth token retrieved when signed-in. + * It is good for 6-months from the last service call. + */ + private volatile String authToken = null; + + /** Weak reference to the sign-in activity, needed during the obtain permissions flow. */ + private WeakReference activityWeakReference = null; + + /** AWSConfiguration object. */ + private AWSConfiguration awsConfiguration = null; + + /** list to store permissions. **/ + private static ArrayList loginScopeList = new ArrayList(); + + /** + * Constructor. Builds the Google Api Client. + * @param context context. + * @param configuration the AWS Configuration. + */ + @Override + public void initialize(@NonNull final Context activityContext, + final AWSConfiguration awsConfig) { + this.context = activityContext; + this.awsConfiguration = awsConfig; + Log.d(LOG_TAG, "Initializing Google SDK..."); + + GoogleSignInOptions.Builder builder = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN); + synchronized (GoogleSignInProvider.loginScopeList) { + for (String loginScope : loginScopeList) { + builder = builder.requestScopes(new Scope(loginScope)); + } + } + final GoogleSignInOptions gso = builder.requestEmail().requestProfile().build(); + Log.d(LOG_TAG, "Created Google SignInOptions."); + + // Build GoogleApiClient with access to basic profile + mGoogleApiClient = new GoogleApiClient.Builder(context) + .addApi(Auth.GOOGLE_SIGN_IN_API, gso) + .build(); + mGoogleApiClient.connect(); + Log.d(LOG_TAG, "Connected to the Google SignIn API Client."); + } + + /** {@inheritDoc} */ + @Override + public String getDisplayName() { + return "Google"; + } + + /** {@inheritDoc} */ + @Override + public boolean refreshUserSignInState() { + final OptionalPendingResult opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient); + + if (opr.isDone()) { + // If the user's cached credentials are valid, the OptionalPendingResult will be "done" + // and the GoogleSignInResult will be available instantly. + + final GoogleSignInResult result = opr.get(); + if (result == null) { + Log.d(LOG_TAG, "GoogleSignInResult is null. Not signed-in with Google."); + return false; + } + + return handleGoogleSignInResultForIsUserSignedIn(result); + } + + final GoogleApiAvailability api = GoogleApiAvailability.getInstance(); + final int code = api.isGooglePlayServicesAvailable(context.getApplicationContext()); + if (ConnectionResult.SUCCESS == code) { + // If the user has not previously signed in on this device or the sign-in has expired, + // this asynchronous branch will attempt to sign in the user silently. Cross-device + // single sign-on will occur in this branch. + final GoogleSignInResult googleSignInResult = opr.await(); + return handleGoogleSignInResultForIsUserSignedIn(googleSignInResult); + } + + Log.w(LOG_TAG, "Google Play Services are not available. Assuming not signed-in with Google."); + return false; + } + + /** + * Add the login permisisons needed by the application. + * The input has to be a list of strings, where each + * string represents a permisison. + * + * Eg: + * GoogleSignInProvider.setPermissions(Scopes.EMAIL); + * GoogleSignInProvider.setPermissions(Scopes.EMAIL, + * Scopes.PROFILE); + * @param loginScopes The list of permissions required + */ + public static void setPermissions(final String... loginScopes) { + synchronized (GoogleSignInProvider.loginScopeList) { + for (String scope : loginScopes) { + GoogleSignInProvider.loginScopeList.add(scope); + } + } + } + + /** {@inheritDoc} */ + @Override + public String getCognitoLoginKey() { + return COGNITO_LOGIN_KEY_GOOGLE; + } + + /** {@inheritDoc} */ + @Override + public String getToken() { + return authToken; + } + + /** {@inheritDoc} */ + @Override + public String refreshToken() { + Log.d(LOG_TAG, "Google provider refreshing token..."); + + try { + authToken = getGoogleAuthToken(signedInAccount.getEmail()); + } catch (final Exception ex) { + Log.w(LOG_TAG, "Failed to update Google token", ex); + authToken = null; + } + return authToken; + } + + /** {@inheritDoc} */ + @Override + public void signOut() { + Log.d(LOG_TAG, "Google provider signing out..."); + + final Status status = Auth.GoogleSignInApi.signOut(mGoogleApiClient).await(); + Log.d(LOG_TAG, "signOut:onResult:" + status); + authToken = null; + } + + /** {@inheritDoc} */ + @Override + public boolean isRequestCodeOurs(final int requestCode) { + return (requestCode == RC_SIGN_IN); + } + + /** {@inheritDoc} */ + @Override + public void handleActivityResult(final int requestCode, + final int resultCode, + final Intent data) { + if (requestCode == RC_SIGN_IN) { + signingIn = false; + + // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); + final GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); + if (result == null) { + // This should not happen based on Google's documentation. + final String errMsg = "GoogleSignInResult was null."; + Log.wtf(LOG_TAG, errMsg); + resultsHandler.onError(GoogleSignInProvider.this, new IllegalStateException(errMsg)); + return; + } + + if (!result.isSuccess()) { + // if the user canceled + if (GoogleSignInStatusCodes.SIGN_IN_CANCELLED == result.getStatus().getStatusCode()) { + resultsHandler.onCancel(GoogleSignInProvider.this); + return; + } + + // If there was a failure, forward it along. + resultsHandler.onError(GoogleSignInProvider.this, + new GoogleSignInException(result)); + } + + Log.i(LOG_TAG, "Successful GoogleSignInResult, status=" + result.getStatus().toString()); + + new Thread(new Runnable() { + @Override + public void run() { + try { + handleGoogleSignInSuccessResult(result); + Log.d(LOG_TAG, "Google provider sign-in succeeded!"); + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + resultsHandler.onSuccess(GoogleSignInProvider.this); + } + }); + } catch (final Exception ex) { + final String errMsg = "Error retrieving Google token."; + Log.e(LOG_TAG, errMsg); + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + resultsHandler.onError(GoogleSignInProvider.this, ex); + } + }); + } + } + }).start(); + } + } + + /** {@inheritDoc} */ + @Override + public View.OnClickListener initializeSignInButton(final Activity signInActivity, + final View buttonView, + final SignInProviderResultHandler providerResultsHandler) { + this.resultsHandler = providerResultsHandler; + final GoogleApiAvailability api = GoogleApiAvailability.getInstance(); + final int code = api.isGooglePlayServicesAvailable(context.getApplicationContext()); + + if (ConnectionResult.SUCCESS != code) { + if (api.isUserResolvableError(code)) { + Log.w(LOG_TAG, "Google Play services recoverable error."); + api.showErrorDialogFragment(signInActivity, code, REQUEST_GOOGLE_PLAY_SERVICES); + } else { + final boolean isDebugBuild = + (0 != (signInActivity + .getApplicationContext() + .getApplicationInfo() + .flags & ApplicationInfo.FLAG_DEBUGGABLE)); + + if (!isDebugBuild) { + buttonView.setVisibility(View.GONE); + } else { + Log.w(LOG_TAG, "Google Play Services are not available, " + + "but we are showing the Google Sign-in Button, anyway, " + + "because this is a debug build."); + } + } + return null; + } + + final View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(final View v) { + if (!signingIn) { + signingIn = true; + if (getPermissionsIfNecessary(signInActivity)) { + return; + } + + initiateGoogleSignIn(signInActivity); + } + } + }; + buttonView.setOnClickListener(listener); + return listener; + } + + /** + * Get reference to the SignedIn Account. + * @return the Google SignIn Account + */ + public GoogleSignInAccount getSignedInAccount() { + return signedInAccount; + } + + /** {@inheritDoc} */ + @Override + public int getPermissionRequestCode() { + return GET_ACCOUNTS_PERMISSION_REQUEST_CODE; + } + + /** {@inheritDoc} */ + @Override + public void handleRequestPermissionsResult(final int requestCode, + final String[] permissions, + final int[] grantResults) { + try { + if (requestCode == GET_ACCOUNTS_PERMISSION_REQUEST_CODE) { + if (grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // The activity will always still be available when the permissions result is returned since + // it will come back on the main thread to the containing activity. + Activity activity = activityWeakReference.get(); + if (activity == null) { + throw new Exception("Cannot initiate GoogleSignIn." + + " Activity context is null"); + } + initiateGoogleSignIn(activity); + } else { + Log.i(LOG_TAG, "Permissions not granted for Google sign-in."); + signingIn = false; + } + } + } catch (final Exception exception) { + Log.e(LOG_TAG, "Cannot initiate GoogleSignIn. Check your permissions.", exception); + } + } + + private boolean handleGoogleSignInResultForIsUserSignedIn(@NonNull final GoogleSignInResult result) { + final String accountEmail; + signedInAccount = result.getSignInAccount(); + if (signedInAccount != null) { + accountEmail = signedInAccount.getEmail(); + } else { + Log.i(LOG_TAG, "GoogleSignInResult indicates not signed in with an account: " + + result.getStatus().toString()); + authToken = null; + return false; + } + + Log.d(LOG_TAG, "Google sign-in was cached, attempting to retrieve auth token."); + try { + authToken = getGoogleAuthToken(accountEmail); + return true; + } catch (final Exception ex) { + Log.w(LOG_TAG, "Couldn't obtain Google Auth token for account.", ex); + return false; + } + } + + private boolean getPermissionsIfNecessary(final Activity activity) { + if (ContextCompat.checkSelfPermission(activity.getApplicationContext(), + Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) { + this.activityWeakReference = new WeakReference(activity); + ActivityCompat.requestPermissions(activity, + new String[]{Manifest.permission.GET_ACCOUNTS}, + GET_ACCOUNTS_PERMISSION_REQUEST_CODE); + return true; + } + + return false; + } + + private void initiateGoogleSignIn(final Activity signInActivity) { + Log.d(LOG_TAG, "Launching sign-in activity."); + final Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); + signInActivity.startActivityForResult(signInIntent, RC_SIGN_IN); + } + + private void handleGoogleSignInSuccessResult(@NonNull final GoogleSignInResult result) throws + IOException, GoogleAuthException, GoogleSignInException { + + final String accountEmail; + signedInAccount = result.getSignInAccount(); + if (signedInAccount != null) { + accountEmail = signedInAccount.getEmail(); + } else { + Log.i(LOG_TAG, "GoogleSignInResult indicates not signed in with an account."); + final GoogleSignInException ex = new GoogleSignInException(result); + Log.d(LOG_TAG, ex.getMessage(), ex); + + authToken = null; + throw ex; + } + + authToken = getGoogleAuthToken(accountEmail); + } + + private String getGoogleClientId() throws IOException { + Log.d(LOG_TAG, "Getting Google Client Id from AWSConfiguration..."); + final String clientId; + + try { + clientId = awsConfiguration.optJsonObject("GoogleSignIn").getString("ClientId-WebApp"); + Log.d(LOG_TAG, "clientId=" + clientId); + return clientId; + } catch (Exception exception) { + throw new IllegalArgumentException("Couldn't find Google ClientId from the AWSConfiguration." + + "Please check the awsconfiguration.json file", exception); + } + } + + private String getGoogleAuthToken(final String accountEmail) throws GoogleAuthException, IOException { + Log.d(LOG_TAG, "Google provider getting token..."); + + final Account googleAccount = new Account(accountEmail, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE); + final String scopes = "audience:server:client_id:" + getGoogleClientId(); + + // Retrieve the Google token. + final String token = GoogleAuthUtil.getToken(context, googleAccount, scopes); + // UserRecoverableAuthException will be thrown from GoogleAuthUtil.getToken() if not signed in. + + if (token != null) { + Log.d(LOG_TAG, "Google Token is OK. Token hashcode = " + token.hashCode()); + } else { + Log.d(LOG_TAG, "Google Token is NULL."); + } + + return token; + } +} diff --git a/aws-android-sdk-auth-google/src/main/res/drawable-hdpi/google_icon.png b/aws-android-sdk-auth-google/src/main/res/drawable-hdpi/google_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b3b5961efdaaf0c7fc0b7e84849472629111645a GIT binary patch literal 6056 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 VhbepfbRGZz002ovPDHLkV1f{w8Os0w literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-google/src/main/res/drawable-tvdpi/google_icon.png b/aws-android-sdk-auth-google/src/main/res/drawable-tvdpi/google_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e3910731413300d9b009da2523dad2adcc5609ae GIT binary patch literal 5123 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 l;Qoo=oOoBiqzZ)?$aHQ?{A1%m8htK+6*IY=@K~ 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 literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-google/src/main/res/drawable-xxxhdpi/google_icon.png b/aws-android-sdk-auth-google/src/main/res/drawable-xxxhdpi/google_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0c3fe17c310e89cc422f8ea6256784f79c85f8bc GIT binary patch literal 29568 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 IM6N<$f^>mqp#T5? literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-google/src/main/res/values/strings.xml b/aws-android-sdk-auth-google/src/main/res/values/strings.xml new file mode 100644 index 00000000000..01e75496f42 --- /dev/null +++ b/aws-android-sdk-auth-google/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Sign in with Google + diff --git a/aws-android-sdk-auth-ui/pom.xml b/aws-android-sdk-auth-ui/pom.xml new file mode 100644 index 00000000000..afd91e17b7c --- /dev/null +++ b/aws-android-sdk-auth-ui/pom.xml @@ -0,0 +1,114 @@ + + 4.0.0 + com.amazonaws + aws-android-sdk-auth-ui + aar + AWS SDK for Android - AWS Authentication UI + The AWS Android SDK for Authentication UI holds the client classes that are used for presenting the SignIn Screen with SignInButtons and Amazon Cognito UserPools UI + http://aws.amazon.com/sdkforandroid + + + + UTF-8 + + + UTF-8 + + + + + com.amazonaws + aws-android-sdk-pom + 2.6.0 + + + + + android-support + file://${env.ANDROID_HOME}/extras/android/m2repository/ + + + + + + com.amazonaws + aws-android-sdk-auth-core + false + 2.6.0 + aar + + + com.amazonaws + aws-android-sdk-auth-google + true + 2.6.0 + aar + + + com.amazonaws + aws-android-sdk-auth-facebook + true + 2.6.0 + aar + + + com.amazonaws + aws-android-sdk-auth-userpools + true + 2.6.0 + aar + + + com.google.android + android + 4.1.1.4 + provided + + + com.android.support + support-v4 + 23.0.0 + aar + + + com.android.support + appcompat-v7 + 23.0.0 + aar + + + com.android.support + cardview-v7 + 23.0.0 + aar + + + + + + + com.simpligility.maven.plugins + android-maven-plugin + 4.5.0 + true + + + 25 + 19.1.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + diff --git a/aws-android-sdk-auth-ui/src/main/AndroidManifest.xml b/aws-android-sdk-auth-ui/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..69bb55f53a9 --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + diff --git a/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/AuthUIConfiguration.java b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/AuthUIConfiguration.java new file mode 100644 index 00000000000..2fac8264f25 --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/AuthUIConfiguration.java @@ -0,0 +1,204 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.ui; + +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; + +import com.amazonaws.mobile.auth.core.signin.ui.buttons.SignInButton; + +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + +/** + * Stores Configuration information related to the SignIn UI screen. + */ +public final class AuthUIConfiguration { + + /** + * Key for Background Color. + */ + private static final String CONFIG_KEY_SIGN_IN_BACKGROUND_COLOR = "signInBackgroundColor"; + + /** + * Key for Resource Identifier of the Logo Image. + */ + private static final String CONFIG_KEY_SIGN_IN_IMAGE_RESOURCE_ID = "signInImageResId"; + + /** + * Key for UserPools. + */ + private static final String CONFIG_KEY_ENABLE_USER_POOLS = "signInUserPoolsEnabled"; + + /** + * Key for SignInButtons. + */ + private static final String CONFIG_KEY_SIGN_IN_BUTTONS = "signInButtons"; + + /** + * Map to store the key and the corresponding objects. + */ + private final Map config; + + /** + * Constructor. + * + * @param config The Configuration Map + */ + private AuthUIConfiguration(final Map config) { + this.config = config; + } + + /** + * Returns the resource identifier of the logo image if set by the user. + * Else, returns the resource identifier of the default logo image + * passed in. + * + * @param defaultResourceId The Resource identifier for the default logo image + * @return The resource identifier set in config or the default passed in + */ + public int getSignInImageResourceId(final int defaultResourceId) { + final Integer resId = (Integer) config.get(CONFIG_KEY_SIGN_IN_IMAGE_RESOURCE_ID); + if (resId == null) { + return defaultResourceId; + } + return resId; + } + + /** + * Returns the background color chosen by the user. + * Else, returns the default background color passed in. + * + * @param defaultBackgroundColor The Default Background color + * @return The background color set in config or the default passed in + */ + public int getSignInBackgroundColor(final int defaultBackgroundColor) { + final Integer backgroundColor = (Integer) config.get(CONFIG_KEY_SIGN_IN_BACKGROUND_COLOR); + if (backgroundColor == null) { + return defaultBackgroundColor; + } + return backgroundColor; + } + + /** + * Checks if userpools is enabled by the user. + * @return True if UserPools is enabled + */ + public boolean getSignInUserPoolsEnabled() { + Object userPoolsEnabled = config.get(CONFIG_KEY_ENABLE_USER_POOLS); + if (userPoolsEnabled != null) { + return (Boolean) userPoolsEnabled; + } else { + return false; + } + } + + /** + * Gets the list of the SignInButton classes configured. + * @return The list of SignInButton classes + */ + public ArrayList> getSignInButtons() { + return (ArrayList) config.get(CONFIG_KEY_SIGN_IN_BUTTONS); + } + + + /** + * Class for building the AWSMobileAuthUIConfiguration object + * + * For example, create the config object with specific attributes. + * + * AuthUIConfiguration config = + * new AuthUIConfiguration.Builder() + * .userPools(true) + * .logoResId(R.drawable.logo_image) + * .signInButton(CustomSignInButton.class) + * .build(); + */ + public static class Builder { + + /** Local object for storing the configuration. */ + private final Map configuration = new HashMap(); + + /** Constructor. */ + public Builder() { } + + /** + * The Resource Identifier for the logo image passed by the user + * is stored in the config map. + * + * @param logoResId The Resource identifier for the logo image + * @return builder + */ + public Builder logoResId(@DrawableRes final int logoResId) { + configuration.put(CONFIG_KEY_SIGN_IN_IMAGE_RESOURCE_ID, logoResId); + return this; + } + + /** + * The Background color that is dislayed on the first half + * of the SignIn Screen. + * + * @param color The Background color + * @return builder + */ + public Builder backgroundColor(final int color) { + configuration.put(CONFIG_KEY_SIGN_IN_BACKGROUND_COLOR, color); + return this; + } + + /** + * Invoke this method in order to enable userpools. + * + * @param enabledUserPools Flag that indicates if the userpools is enabled or not + * @return builder + */ + public Builder userPools(final boolean enabledUserPools) { + configuration.put(CONFIG_KEY_ENABLE_USER_POOLS, enabledUserPools); + return this; + } + + /** + * Add a SignInButton to the SignIn Screen by passing in the Class + * of the button that inherits from the SignInButton. + * + * @param signInButton Button Class that inherits from the SignInButton + * @return builder + */ + public Builder signInButton(@NonNull final Class signInButton) { + ArrayList> signInButtonList; + if (configuration.get(CONFIG_KEY_SIGN_IN_BUTTONS) == null) { + signInButtonList = new ArrayList>(); + signInButtonList.add(signInButton); + configuration.put(CONFIG_KEY_SIGN_IN_BUTTONS, signInButtonList); + } else { + signInButtonList = (ArrayList) configuration.get(CONFIG_KEY_SIGN_IN_BUTTONS); + signInButtonList.add(signInButton); + } + return this; + } + + /** + * Builds the AuthUIConfiguration object. + * @return the AuthUIConfiguration created by the parts provided + */ + public AuthUIConfiguration build() { + return new AuthUIConfiguration(configuration); + } + } +} diff --git a/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInActivity.java b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInActivity.java new file mode 100644 index 00000000000..97b0a187f3a --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInActivity.java @@ -0,0 +1,190 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.ui; + +import android.content.Intent; +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; + +import com.amazonaws.mobile.auth.core.IdentityManager; +import com.amazonaws.mobile.auth.core.IdentityProvider; +import com.amazonaws.mobile.auth.core.SignInResultHandler; +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.SignInProviderResultHandler; + +import java.util.HashMap; +import java.util.UUID; + + +/** + * Activity for handling Sign-in with an Identity Provider. + */ +public class SignInActivity extends AppCompatActivity { + + /** Log Tag. */ + private static final String LOG_TAG = SignInActivity.class.getSimpleName(); + + /** Reference to the singleton instance of SignInManager. */ + private SignInManager signInManager; + + /** Stores the UI Configuration object passed into SignInActivity. */ + static HashMap configurationStore + = new HashMap(); + + /** + * SignInProviderResultHandlerImpl handles the final result from sign in. + */ + private class SignInProviderResultHandlerImpl implements SignInProviderResultHandler { + /** + * Receives the successful sign-in result and starts the main activity. + * + * @param provider the identity provider used for sign-in. + */ + @Override + public void onSuccess(final IdentityProvider provider) { + Log.i(LOG_TAG, String.format(getString(R.string.sign_in_succeeded_message_format), + provider.getDisplayName())); + + // The sign-in manager is no longer needed once signed in. + SignInManager.dispose(); + final SignInResultHandler signInResultsHandler = signInManager.getResultHandler(); + + // Call back the results handler. + signInResultsHandler.onSuccess(SignInActivity.this, provider); + finish(); + } + + /** + * Receives the sign-in result indicating the user canceled and shows a toast. + * + * @param provider the identity provider with which the user attempted sign-in. + */ + @Override + public void onCancel(final IdentityProvider provider) { + Log.i(LOG_TAG, String.format(getString(R.string.sign_in_canceled_message_format), + provider.getDisplayName())); + signInManager.getResultHandler() + .onIntermediateProviderCancel(SignInActivity.this, provider); + } + + /** + * Receives the sign-in result that an error occurred signing in and shows a toast. + * + * @param provider the identity provider with which the user attempted sign-in. + * @param ex the exception that occurred. + */ + @Override + public void onError(final IdentityProvider provider, final Exception ex) { + Log.e(LOG_TAG, String.format("Sign-in with %s caused an error.", provider.getDisplayName()), ex); + signInManager.getResultHandler() + .onIntermediateProviderError(SignInActivity.this, provider, ex); + } + } + + /** + * This method is called when SignInActivity is created. + * Get the instance of SignInManager and set the callback + * to be received from SignInManager on signin. + */ + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + signInManager = SignInManager.getInstance(); + if (signInManager == null) { + Log.e(LOG_TAG, "Invoke SignInActivity.startSignInActivity() method to create the SignInManager."); + return; + } + signInManager.setProviderResultsHandler(this, new SignInProviderResultHandlerImpl()); + setContentView(R.layout.activity_sign_in); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + public void onRequestPermissionsResult(final int requestCode, + @NonNull final String[] permissions, + @NonNull final int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + signInManager.handleRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + protected void onActivityResult(final int requestCode, + final int resultCode, + final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + signInManager.handleActivityResult(requestCode, resultCode, data); + } + + @Override + public void onBackPressed() { + if (signInManager.getResultHandler().onCancel(this)) { + super.onBackPressed(); + // Since we are leaving sign-in via back, we can dispose the sign-in manager, since sign-in was cancelled. + SignInManager.dispose(); + } + } + + /** + * Start the SignInActivity that kicks off the authentication flow + * by initializing the SignInManager. + * + * @param context The context from which the SignInActivity will be started + * @param config Reference to AuthUIConfiguration object + */ + public static void startSignInActivity(@NonNull final Context context, + final AuthUIConfiguration config) { + try { + String uuid = UUID.randomUUID().toString(); + synchronized (configurationStore) { + configurationStore.put(uuid, config); + } + Intent intent = new Intent(context, SignInActivity.class); + intent.putExtra(SignInView.CONFIGURATION_KEY, uuid); + intent.putExtra(SignInView.BACKGROUND_COLOR_KEY, + config.getSignInBackgroundColor(SignInView.DEFAULT_BACKGROUND_COLOR)); + context.startActivity(intent); + } catch (Exception exception) { + Log.e(LOG_TAG, "Cannot start the SignInActivity. " + + "Check the context and the configuration object passed in.", exception); + } + } + + /** + * Start the SignInActivity that kicks off the authentication flow + * by initializing the SignInManager. + * + * @param context The context from which the SignInActivity will be started + */ + public static void startSignInActivity(@NonNull final Context context) { + try { + Intent intent = new Intent(context, SignInActivity.class); + context.startActivity(intent); + } catch (Exception exception) { + Log.e(LOG_TAG, "Cannot start the SignInActivity. " + + "Check the context and the configuration object passed in.", exception); + } + } +} diff --git a/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInView.java b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInView.java new file mode 100644 index 00000000000..d1fd9add12e --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/SignInView.java @@ -0,0 +1,533 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.ui; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.amazonaws.mobile.auth.core.signin.ui.SplitBackgroundDrawable; +import com.amazonaws.mobile.auth.core.signin.ui.buttons.SignInButton; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; + +/** + * View for displaying sign-in components. + */ +public class SignInView extends LinearLayout { + + /** Log Tag. */ + private static final String LOG_TAG = SignInView.class.getSimpleName(); + + /** Height of the logo image. */ + private static final int MAX_IMAGE_HEIGHT = dp(250); + + /** Margins of the logo image. */ + private static final int IMAGE_MARGINS = dp(20); + + /** Margins for the Image Layout that holds the logo image. */ + private static final int IMAGE_LAYOUT_MARGINS = dp(10); + + /** String that represents the SDK Version. */ + private static final String SDK_VERSION = "2.6.0"; + + /** Common Prefix of the namespaces of different SignIn providers. */ + private static final String NAMESPACE_COMMON_PREFIX = "com.amazonaws.mobile.auth"; + + /** Group name. */ + private static final String AWS_MOBILE_AUTH_GROUP_NAME = "com.amazonaws"; + + /** Dependency name for UserPool SignIn View class. */ + private static final String USER_POOL_SIGN_IN_VIEW = NAMESPACE_COMMON_PREFIX + ".userpools.UserPoolSignInView"; + + /** Dependency name for FormView of UserPool SignIn. */ + private static final String FORM_VIEW = NAMESPACE_COMMON_PREFIX + ".userpools.FormView"; + + /** Dependency name for UserPool SignIn package. */ + private static final String USER_POOL_SIGN_IN_IMPORT = AWS_MOBILE_AUTH_GROUP_NAME + + ":aws-android-sdk-auth-userpools:" + + SDK_VERSION; + + /** Dependency name for Facebook Button class. */ + private static final String FACEBOOK_BUTTON = NAMESPACE_COMMON_PREFIX + ".facebook.buttons.FacebookButton"; + + /** Dependency name for Facebook SignIn package. */ + private static final String FACEBOOK_SIGN_IN_IMPORT = AWS_MOBILE_AUTH_GROUP_NAME + + ":aws-android-sdk-auth-facebook:" + + SDK_VERSION; + + /** Dependency name for Google Button class. */ + private static final String GOOGLE_BUTTON = NAMESPACE_COMMON_PREFIX + ".google.buttons.GoogleButton"; + + /** Dependency name for Google SignIn package. */ + private static final String GOOGLE_SIGN_IN_IMPORT = AWS_MOBILE_AUTH_GROUP_NAME + + ":aws-android-sdk-auth-google:" + + SDK_VERSION; + + /** Package Name for AuthUI. */ + private static final String PACKAGE_NAME = "com.amazonaws.mobile.auth.ui"; + + /** SignIn Background Color Key. **/ + public static final String BACKGROUND_COLOR_KEY = "signInBackgroundColor"; + + /** Configuration Key to store AuthUIConfiguration objects. */ + public static final String CONFIGURATION_KEY = "com.amazonaws.mobile.auth.ui.configurationkey"; + + /** Default Background Color. */ + public static final int DEFAULT_BACKGROUND_COLOR = Color.DKGRAY; + + /** Resource Identitifer for default Logo Image. */ + public static final int DEFAULT_LOGO_IMAGE_RES_ID = R.drawable.default_sign_in_logo; + + /** Image View. */ + private ImageView imageView; + + /** Divider in the SignIn screen. */ + private View divider; + + /** Reference to the SplitBackgroundDrawable. */ + private SplitBackgroundDrawable splitBackgroundDrawable; + + /** Margins for the SignIn Button. */ + private int signInButtonMargin; + + /** Width for the SignIn Button. */ + private int signInButtonWidth; + + /** Height for the SignIn Button. */ + private int signInButtonHeight; + + /** Reference to the AuthUIConfiguration. */ + private AuthUIConfiguration config = null; + + /** Reference to the UserPoolsSignInView. */ + private Object userPoolsSignInView = null; + + /** Stores the list of SignInButtons. */ + private ArrayList buttonStore = null; + + /** Resource Identifier for the logo image. */ + private int logoResId; + + /** Background Color. */ + private int backgroundColor; + + /** + * Consructor. + * @param context Activity Context + */ + public SignInView(@NonNull final Context context) { + this(context, null); + } + + /** + * Constructor. + * @param context Activity Context + * @param attrs Attribute Set + */ + public SignInView(@NonNull final Context context, + @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Sets up the logo image and the background color. + */ + private void setUpLogoAndBackgroundColor() { + /** + * Get default background color and image resource ids. + */ + this.logoResId = DEFAULT_LOGO_IMAGE_RES_ID; + this.backgroundColor = DEFAULT_BACKGROUND_COLOR; + + Log.d(LOG_TAG, "Using defaults: backgroundColor = " + + backgroundColor + "; logoResId = " + logoResId); + + /** + * Read in the image resource id and background color + * from the configuration if present. + */ + if (!isInEditMode()) { + if (this.config != null) { + this.logoResId = this.config.getSignInImageResourceId(logoResId); + this.backgroundColor = this.config.getSignInBackgroundColor(backgroundColor); + } + } + + Log.d(LOG_TAG, "Background Color : " + this.backgroundColor); + Log.d(LOG_TAG, "Logo : " + this.logoResId); + } + + /** + * Sets up the Splitter and background drawable. + */ + private void setUpSplitBackgroundDrawable() { + if (this.config != null && this.config.getSignInUserPoolsEnabled()) { + splitBackgroundDrawable = new SplitBackgroundDrawable(0, backgroundColor); + } else { + splitBackgroundDrawable = new SplitBackgroundDrawable(0); + } + setBackgroundDrawable(splitBackgroundDrawable); + } + + /** + * Sets up the image view that displays the logo image. + * @param context The activity context. + */ + private void setUpImageView(@NonNull final Context context) { + /** + * ImageView that holds the logo image. + */ + imageView = new ImageView(context); + imageView.setImageResource(logoResId); + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + imageView.setAdjustViewBounds(true); + + /** + * Layout for the image view. + */ + final LinearLayout.LayoutParams imageLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + imageLayoutParams.setMargins(IMAGE_LAYOUT_MARGINS, 0, IMAGE_LAYOUT_MARGINS, 0); + addView(imageView, imageLayoutParams); + } + + /** + * Sets up the UserPools UI with the Email and Password FormView. + * @param context The activity context. + */ + private void setUpUserPools(@NonNull final Context context) { + /** + * Use Reflection for UserPoolSignIn dependency. + */ + if (this.config != null && this.config.getSignInUserPoolsEnabled()) { + Log.d(LOG_TAG, "Trying to create an instance of UserPoolSignInView"); + + userPoolsSignInView = createDependencyObject(USER_POOL_SIGN_IN_VIEW, context, USER_POOL_SIGN_IN_IMPORT); + if (userPoolsSignInView != null) { + final LinearLayout.LayoutParams userPoolLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + addView((View) userPoolsSignInView, userPoolLayoutParams); + } + } + } + + /** + * Sets up the divider that divides the UserPools UI and the SignInButtons. + * @param context The activity context. + */ + private void setUpDivider(@NonNull final Context context) { + /** + * Create "--or sign in with--" divider if userpools is configured. + * Else create "--sign in with--" divider. + */ + if (this.config != null && this.config.getSignInUserPoolsEnabled()) { + divider = inflate(context, R.layout.horizontal_or_sign_in_divider, null); + } else { + divider = inflate(context, R.layout.horizontal_sign_in_divider, null); + } + addView(divider); + } + + /** + * Sets up the SignIn Buttons. + * @param context The activity context. + */ + private void setUpSignInButtons(@NonNull final Context context) { + /** + * Get the height, width and margins for the sign in buttons. + */ + signInButtonMargin = getResources().getDimensionPixelSize(R.dimen.sign_in_button_margin); + signInButtonWidth = getResources().getDimensionPixelSize(R.dimen.sign_in_button_width); + signInButtonHeight = getResources().getDimensionPixelSize(R.dimen.sign_in_button_height); + + /** + * Add the signInButtons configured to the view. + */ + this.addSignInButtonsToView(context); + + /** + * There are two conditions on which the divider is set. + * + * 1. If UserPools is configured and one or more buttons are added. + * 2. If One of more buttons are added. + */ + divider.setVisibility(GONE); + if (this.buttonStore.size() > 0) { + divider.setVisibility(VISIBLE); + } + } + + /** + * Constructor. + * @param context Activity Context + * @param attrs Attribute Set + * @param defStyleAttr Default Style Attribute + */ + public SignInView(@NonNull final Context context, + @Nullable final AttributeSet attrs, + final int defStyleAttr) { + super(context, attrs, defStyleAttr); + + this.setOrientation(VERTICAL); + this.setGravity(Gravity.CENTER_HORIZONTAL); + this.buttonStore = new ArrayList(); + this.config = getConfiguration(context); + + this.setUpLogoAndBackgroundColor(); + this.setUpSplitBackgroundDrawable(); + this.setUpImageView(context); + this.setUpUserPools(context); + this.setUpDivider(context); + this.setUpSignInButtons(context); + } + + /** {@inheritDoc} */ + @Override + protected void onMeasure(final int widthMeasureSpec, + final int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + resizeImageView(); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + /** {@inheritDoc} */ + @Override + protected void onLayout(final boolean changed, + final int l, + final int t, + final int r, + final int b) { + super.onLayout(changed, l, t, r, b); + + /** Find the split point for the background image, so each half gets a different color. */ + if (this.config != null && this.config.getSignInUserPoolsEnabled()) { + if (userPoolsSignInView != null) { + View view = (View) userPoolsSignInView; + Object formViewObject = invokeGetCredentialsFormView(USER_POOL_SIGN_IN_VIEW, + userPoolsSignInView, + USER_POOL_SIGN_IN_IMPORT); + final int measuredHeight = ((View) formViewObject).getMeasuredHeight(); + final int splitPoint = view.getTop() + (measuredHeight / 2); + splitBackgroundDrawable.setSplitPointDistanceFromTop(splitPoint); + } + } else { + final int splitPoint = imageView.getTop() + imageView.getMeasuredHeight(); + splitBackgroundDrawable.setSplitPointDistanceFromTop(splitPoint); + } + } + + /** + * Resizes the image view based on the UI Configuration. + */ + private void resizeImageView() { + int availableHeight = getAvailableHeight(); + int availableWidth = getMeasuredWidth(); + + int dimension = Math.min(availableHeight, availableWidth); + imageView.getLayoutParams().height = dimension; + imageView.getLayoutParams().width = dimension; + + ((LayoutParams) imageView.getLayoutParams()).setMargins( + IMAGE_MARGINS, IMAGE_MARGINS, IMAGE_MARGINS, IMAGE_MARGINS); + imageView.setLayoutParams(imageView.getLayoutParams()); + } + + /** + * Gets the available height based on the UserPools UI and SignIn Buttons. + * @return The available height in the view + */ + private int getAvailableHeight() { + int availableHeight = getMeasuredHeight(); + + if (this.config != null && this.config.getSignInUserPoolsEnabled()) { + if (userPoolsSignInView != null) { + availableHeight -= ((View) userPoolsSignInView).getMeasuredHeight(); + } + } + + availableHeight -= divider.getMeasuredHeight(); + + final int count = this.buttonStore.size(); + if (count > 0) { + for (SignInButton button : this.buttonStore) { + final int buttonHeight = ((View) button).getMeasuredHeight(); + availableHeight -= buttonHeight; + availableHeight -= (2 * signInButtonMargin); + } + } + + /** Subtract the top and bottom image margins. */ + availableHeight -= (2 * IMAGE_MARGINS); + + /** Leave a space at least equal to the size of the sign-in button margin on the bottom of the view. */ + availableHeight -= signInButtonMargin; + + if (availableHeight > MAX_IMAGE_HEIGHT) { + availableHeight = MAX_IMAGE_HEIGHT; + } + + return availableHeight; + } + + /** + * Creates the object for the dependency class specified. + * @param className The class name + * @param methodName The method name + * @param dependency The string that represents the dependency containing the className + * @return The object returned by invoking a method on the class passed in. + */ + private Object createDependencyObject(final String className, + final Context context, + final String dependency) { + try { + Class classObject = Class.forName(className); + Constructor constructor = classObject.getConstructor(Context.class); + Object object = constructor.newInstance(new Object[] { context }); + return object; + } catch (RuntimeException runtimeException) { + throw runtimeException; + } catch (Exception exception) { + Log.e(LOG_TAG, "Couldn't construct the object. Class " + className + " is not found. " + + "Please import the appropriate dependencies: " + dependency, exception); + return null; + } + } + + /** + * Invoke FormView.getCredentialsFormView() function through reflection. + * @param className The class name + * @param viewObject The object passed as parameter + * @param dependencyImport The string that represents the dependency containing the className + * @return The object returned by invoking a method on the class passed in. + * @return The object returned by invoking FormView.getCredentialsFormView() + */ + private Object invokeGetCredentialsFormView(final String className, + final Object viewObject, + final String dependency) { + return invokeReflectedMethod(className, + "getCredentialsFormView", + viewObject, + dependency); + } + + /** + * Invoke a method of a class through reflection. + * @param className The class name + * @param methodName The method name + * @param viewObject The object passed as parameter + * @param dependencyImport The string that represents the dependency containing the className + * @return The object returned by invoking a method on the class passed in. + */ + private Object invokeReflectedMethod(final String className, + final String methodName, + final Object viewObject, + final String dependencyImport) { + + try { + Class formViewClass = Class.forName(className); + Method method = formViewClass.getMethod(methodName, new Class[] {}); + return method.invoke(viewObject); + } catch (Exception exception) { + Log.e(LOG_TAG, "Class " + className + " is not found. Method " + methodName + " is not found." + + "Please import the appropriate dependencies: " + dependencyImport, exception); + return null; + } + } + + /** + * Gets the AuthUIConfiguration from the intent passed in by the activity. + * @param context The activity context. + * @return AuthUIConfiguration The configuration object passed in by the application. + */ + private AuthUIConfiguration getConfiguration(@NonNull final Context context) { + try { + Intent intent = ((Activity) context).getIntent(); + String uuid = (String)(intent.getSerializableExtra(CONFIGURATION_KEY)); + AuthUIConfiguration configuration = SignInActivity.configurationStore.get(uuid); + purgeConfigurationStoreEntry(uuid); + return configuration; + } catch (Exception exception) { + exception.printStackTrace(); + Log.e(LOG_TAG, "Intent is null. Cannot read the configuration from the intent."); + return null; + } + } + + /** + * Remove the UUID, AuthUIConfiguration entry from the configuration store. + * @param uuid The UUID for which the entry should be removed. + */ + private void purgeConfigurationStoreEntry(final String uuid) { + try { + synchronized (SignInActivity.configurationStore) { + SignInActivity.configurationStore.remove(uuid); + } + } catch (Exception exception) { + exception.printStackTrace(); + Log.e(LOG_TAG, "Cannot purge the entry from the configuration store."); + return; + } + } + + /** + * Add SignInButtons to the view. + * @param context The activity context. + */ + private void addSignInButtonsToView(@NonNull final Context context) { + try { + if (this.config != null) { + ArrayList> signInButtons = this.config.getSignInButtons(); + for (Class signInButton : signInButtons) { + SignInButton buttonObject = (SignInButton) createDependencyObject(signInButton.getName(), + context, signInButton.getCanonicalName()); + if (buttonObject != null) { + final LinearLayout.LayoutParams signInButtonLayoutParams + = new LinearLayout.LayoutParams(this.signInButtonWidth, this.signInButtonHeight); + signInButtonLayoutParams.setMargins(0, this.signInButtonMargin, 0, this.signInButtonMargin); + this.buttonStore.add(buttonObject); + addView((View) buttonObject, signInButtonLayoutParams); + } else { + Log.e(LOG_TAG, "Cannot construct an object of SignInButton " + + signInButton.getCanonicalName()); + } + } + } else { + Log.d(LOG_TAG, "Configuration is Null. There are no buttons to add to the view"); + } + } catch (Exception exception) { + exception.printStackTrace(); + Log.e(LOG_TAG, "Cannot access the configuration or error in adding the signin button to the view"); + return; + } + } +} diff --git a/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/package-info.java b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/package-info.java new file mode 100644 index 00000000000..69a155da068 --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/java/com/amazonaws/mobile/auth/ui/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Package Name. + */ +package com.amazonaws.mobile.auth.ui; diff --git a/aws-android-sdk-auth-ui/src/main/res/drawable-hdpi/default_sign_in_logo.png b/aws-android-sdk-auth-ui/src/main/res/drawable-hdpi/default_sign_in_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2d2888be01dc5a57d618880dcba5c0443239d64a GIT binary patch literal 21897 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 literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-ui/src/main/res/drawable-tvdpi/default_sign_in_logo.png b/aws-android-sdk-auth-ui/src/main/res/drawable-tvdpi/default_sign_in_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5af6a1100bf2511ed27accf303b887a6aeb80dcd GIT binary patch literal 16272 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 H)vNykf>U}4 literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-ui/src/main/res/drawable-xxhdpi/default_sign_in_logo.png b/aws-android-sdk-auth-ui/src/main/res/drawable-xxhdpi/default_sign_in_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2f31d683c74d1e1dba2b012ec909fd884c4866bb GIT binary patch literal 65734 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

LOWscz=wk&TBHld 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 literal 0 HcmV?d00001 diff --git a/aws-android-sdk-auth-ui/src/main/res/drawable-xxxhdpi/default_sign_in_logo.png b/aws-android-sdk-auth-ui/src/main/res/drawable-xxxhdpi/default_sign_in_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5b8313cedc6d1fc8c2ef73987faa88407321d5 GIT binary patch literal 103772 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

4rr`r&g72#1{6~R1Gg@Q&EWJK$Ir^gD# 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 + diff --git a/aws-android-sdk-auth-ui/src/main/res/layout/horizontal_or_sign_in_divider.xml b/aws-android-sdk-auth-ui/src/main/res/layout/horizontal_or_sign_in_divider.xml new file mode 100644 index 00000000000..f731eccf613 --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/res/layout/horizontal_or_sign_in_divider.xml @@ -0,0 +1,36 @@ + + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-auth-ui/src/main/res/layout/horizontal_sign_in_divider.xml b/aws-android-sdk-auth-ui/src/main/res/layout/horizontal_sign_in_divider.xml new file mode 100644 index 00000000000..1070f2482ef --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/res/layout/horizontal_sign_in_divider.xml @@ -0,0 +1,36 @@ + + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-auth-ui/src/main/res/values/attrs.xml b/aws-android-sdk-auth-ui/src/main/res/values/attrs.xml new file mode 100644 index 00000000000..0f09a76318a --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/res/values/attrs.xml @@ -0,0 +1,22 @@ + + + + + @android:color/darker_gray + + + @drawable/default_sign_in_logo + + + + + + + + + \ No newline at end of file diff --git a/aws-android-sdk-auth-ui/src/main/res/values/colors.xml b/aws-android-sdk-auth-ui/src/main/res/values/colors.xml new file mode 100644 index 00000000000..8fbc637b5fd --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #DDDDDD + diff --git a/aws-android-sdk-auth-ui/src/main/res/values/dimens.xml b/aws-android-sdk-auth-ui/src/main/res/values/dimens.xml new file mode 100644 index 00000000000..3811ba897b9 --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ + + + 10dp + 240dp + 40dp + 2dp + diff --git a/aws-android-sdk-auth-ui/src/main/res/values/strings.xml b/aws-android-sdk-auth-ui/src/main/res/values/strings.xml new file mode 100644 index 00000000000..3ccb67746a7 --- /dev/null +++ b/aws-android-sdk-auth-ui/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + Sign-in + "Sign-in with %s succeeded." + "Sign-in with %s canceled." + or sign in with + Sign in with + diff --git a/aws-android-sdk-auth-userpools/pom.xml b/aws-android-sdk-auth-userpools/pom.xml new file mode 100644 index 00000000000..660f19b27de --- /dev/null +++ b/aws-android-sdk-auth-userpools/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + com.amazonaws + aws-android-sdk-auth-userpools + aar + AWS SDK for Android - AWS Cognito Userpools SignIn + The AWS Android SDK for Authentication - Cognito Userpools SignIn holds the client classes that are used for enabling communication with Cognito UserPools SignIn Provider + http://aws.amazon.com/sdkforandroid + + + + UTF-8 + + + UTF-8 + + + + + com.amazonaws + aws-android-sdk-pom + 2.6.0 + + + + + android-support + file://${env.ANDROID_HOME}/extras/android/m2repository/ + + + + + + com.amazonaws + aws-android-sdk-cognitoidentityprovider + false + 2.6.0 + + + + com.amazonaws + aws-android-sdk-auth-core + false + 2.6.0 + aar + + + + com.google.android + android + 4.1.1.4 + provided + + + + com.android.support + support-v4 + 24.2.0 + aar + + + + + + + com.simpligility.maven.plugins + android-maven-plugin + 4.5.0 + true + + + 25 + 19.1.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + diff --git a/aws-android-sdk-auth-userpools/src/main/AndroidManifest.xml b/aws-android-sdk-auth-userpools/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..0037533195d --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/CognitoUserPoolsSignInProvider.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/CognitoUserPoolsSignInProvider.java new file mode 100644 index 00000000000..1148f31d7a5 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/CognitoUserPoolsSignInProvider.java @@ -0,0 +1,750 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.VerificationHandler; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserAttributes; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserCodeDeliveryDetails; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationDetails; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChallengeContinuation; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ForgotPasswordContinuation; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.ForgotPasswordHandler; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.GenericHandler; +import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.SignUpHandler; + +import com.amazonaws.services.cognitoidentityprovider.model.InvalidParameterException; +import com.amazonaws.services.cognitoidentityprovider.model.NotAuthorizedException; +import com.amazonaws.services.cognitoidentityprovider.model.UserNotConfirmedException; +import com.amazonaws.services.cognitoidentityprovider.model.UserNotFoundException; + +import com.amazonaws.mobile.auth.core.signin.SignInProvider; +import com.amazonaws.mobile.auth.core.signin.SignInProviderResultHandler; +import com.amazonaws.mobile.auth.core.internal.util.ViewHelper; + +import com.amazonaws.regions.Regions; + +import java.util.HashSet; +import java.util.Set; + +/** + * Manages sign-in using Cognito User Pools. + */ +public class CognitoUserPoolsSignInProvider implements SignInProvider { + /** + * Cognito User Pools attributes. + */ + public static final class AttributeKeys { + + /** Username attribute. */ + public static final String USERNAME = "username"; + + /** Password attribute. */ + public static final String PASSWORD = "password"; + + /** Verification code attribute. */ + public static final String VERIFICATION_CODE = "verification_code"; + + /** Given name attribute. */ + public static final String GIVEN_NAME = "given_name"; + + /** Email address attribute. */ + public static final String EMAIL_ADDRESS = "email"; + + /** Phone number attribute. */ + public static final String PHONE_NUMBER = "phone_number"; + + /** Background Color. */ + public static final String BACKGROUND_COLOR = "signInBackgroundColor"; + } + + /** Log tag. */ + private static final String LOG_TAG = CognitoUserPoolsSignInProvider.class.getSimpleName(); + + /** Start of Intent request codes owned by the Cognito User Pools app. */ + private static final int REQUEST_CODE_START = 0x2970; + + /** Request code for password reset Intent. */ + private static final int FORGOT_PASSWORD_REQUEST_CODE = REQUEST_CODE_START + 42; + + /** Request code for account registration Intent. */ + private static final int SIGN_UP_REQUEST_CODE = REQUEST_CODE_START + 43; + + /** Request code for MFA Intent. */ + private static final int MFA_REQUEST_CODE = REQUEST_CODE_START + 44; + + /** Request code for account verification Intent. */ + private static final int VERIFICATION_REQUEST_CODE = REQUEST_CODE_START + 45; + + /** Request codes that the Cognito User Pools can handle. */ + private static final Set REQUEST_CODES = new HashSet() { { + add(FORGOT_PASSWORD_REQUEST_CODE); + add(SIGN_UP_REQUEST_CODE); + add(MFA_REQUEST_CODE); + add(VERIFICATION_REQUEST_CODE); + } }; + + /** Stores the configuration file name. */ + private static final String AWS_CONFIGURATION_FILE = "AWSConfiguration"; + + /** Minimum length of password supported by Cognito. */ + private static final int PASSWORD_MIN_LENGTH = 6; + + /** Prefix of the exception message. */ + private static final String USERPOOLS_EXCEPTION_PREFIX = "(Service"; + + /** The sign-in results adapter from the SignInManager. */ + private SignInProviderResultHandler resultsHandler; + + /** Forgot Password processing provided by the Cognito User Pools SDK. */ + private ForgotPasswordContinuation forgotPasswordContinuation; + + /** MFA processing provided by the Cognito User Pools SDK. */ + private MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation; + + /** Android context. */ + private Context context; + + /** Invoking Android Activity. */ + private Activity activity; + + /** Sign-in username. */ + private String username; + + /** Sign-in password. */ + private String password; + + /** Sign-in verification code. */ + private String verificationCode; + + /** The key for CognitoUserPools Login. */ + private String cognitoLoginKey; + + /** Active Cognito User Pool. */ + private CognitoUserPool cognitoUserPool; + + /** Active Cognito User Pools session. */ + private CognitoUserSession cognitoUserSession; + + /** AWSConfiguration object. */ + private AWSConfiguration awsConfiguration; + + /** Background Color for the View. */ + private int backgroundColor; + + /** + * Handle callbacks from the Forgot Password flow. + */ + private ForgotPasswordHandler forgotPasswordHandler = new ForgotPasswordHandler() { + @Override + public void onSuccess() { + Log.d(LOG_TAG, "Password change succeeded."); + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_forgot_password), + activity.getString(R.string.password_change_success)); + } + + @Override + public void getResetCode(final ForgotPasswordContinuation continuation) { + forgotPasswordContinuation = continuation; + + final Intent intent = new Intent(context, ForgotPasswordActivity.class); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + CognitoUserPoolsSignInProvider.this.backgroundColor); + activity.startActivityForResult(intent, FORGOT_PASSWORD_REQUEST_CODE); + } + + @Override + public void onFailure(final Exception exception) { + Log.e(LOG_TAG, "Password change failed.", exception); + + final String message; + if (exception instanceof InvalidParameterException) { + message = activity.getString(R.string.password_change_no_verification_failed); + } else { + message = getErrorMessageFromException(exception); + } + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_forgot_password), + activity.getString(R.string.password_change_failed) + " " + message); + } + }; + + /** + * Start the SignUp Confirm Activity with the attribte keys. + */ + private void startVerificationActivity() { + final Intent intent = new Intent(context, SignUpConfirmActivity.class); + intent.putExtra(AttributeKeys.USERNAME, username); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + CognitoUserPoolsSignInProvider.this.backgroundColor); + activity.startActivityForResult(intent, VERIFICATION_REQUEST_CODE); + } + + /** + * Handle callbacks from the Sign Up flow. + */ + private SignUpHandler signUpHandler = new SignUpHandler() { + @Override + public void onSuccess(final CognitoUser user, final boolean signUpConfirmationState, + final CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) { + if (signUpConfirmationState) { + Log.d(LOG_TAG, "Signed up. User ID = " + user.getUserId()); + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up), + activity.getString(R.string.sign_up_success) + " " + user.getUserId()); + } else { + Log.w(LOG_TAG, "Additional confirmation for sign up."); + + startVerificationActivity(); + } + } + + @Override + public void onFailure(final Exception exception) { + Log.e(LOG_TAG, "Sign up failed.", exception); + ViewHelper.showDialog(activity, activity.getString(R.string.title_dialog_sign_up_failed), + exception.getLocalizedMessage() != null ? getErrorMessageFromException(exception) + : activity.getString(R.string.sign_up_failed)); + } + }; + + /** + * Handle callbacks from the Sign Up - Confirm Account flow. + */ + private GenericHandler signUpConfirmationHandler = new GenericHandler() { + @Override + public void onSuccess() { + Log.i(LOG_TAG, "Confirmed."); + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up_confirm), + activity.getString(R.string.sign_up_confirm_success)); + } + + @Override + public void onFailure(final Exception exception) { + Log.e(LOG_TAG, "Failed to confirm user.", exception); + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up_confirm), + activity.getString(R.string.sign_up_confirm_failed) + " " + getErrorMessageFromException(exception)); + } + }; + + /** + * Resent the confirmation code on MFA. + */ + private void resendConfirmationCode() { + final CognitoUser cognitoUser = cognitoUserPool.getUser(username); + cognitoUser.resendConfirmationCodeInBackground(new VerificationHandler() { + @Override + public void onSuccess(final CognitoUserCodeDeliveryDetails verificationCodeDeliveryMedium) { + startVerificationActivity(); + } + + @Override + public void onFailure(final Exception exception) { + if (null != resultsHandler) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_in), + activity.getString(R.string.login_failed) + + "\nUser was not verified and resending confirmation code failed.\n" + + getErrorMessageFromException(exception)); + + resultsHandler.onError(CognitoUserPoolsSignInProvider.this, exception); + } + } + }); + } + + /** + * Handle callbacks from the Authentication flow. Includes MFA handling. + */ + private AuthenticationHandler authenticationHandler = new AuthenticationHandler() { + @Override + public void onSuccess(final CognitoUserSession userSession, final CognitoDevice newDevice) { + Log.i(LOG_TAG, "Logged in. " + userSession.getIdToken()); + + cognitoUserSession = userSession; + + if (null != resultsHandler) { + resultsHandler.onSuccess(CognitoUserPoolsSignInProvider.this); + } + } + + @Override + public void getAuthenticationDetails( + final AuthenticationContinuation authenticationContinuation, final String userId) { + + if (null != username && null != password) { + final AuthenticationDetails authenticationDetails = new AuthenticationDetails( + username, + password, + null); + + authenticationContinuation.setAuthenticationDetails(authenticationDetails); + authenticationContinuation.continueTask(); + } + } + + @Override + public void getMFACode(final MultiFactorAuthenticationContinuation continuation) { + multiFactorAuthenticationContinuation = continuation; + + final Intent intent = new Intent(context, MFAActivity.class); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + CognitoUserPoolsSignInProvider.this.backgroundColor); + activity.startActivityForResult(intent, MFA_REQUEST_CODE); + } + + @Override + public void authenticationChallenge(final ChallengeContinuation continuation) { + throw new UnsupportedOperationException("Not supported in this sample."); + } + + @Override + public void onFailure(final Exception exception) { + Log.e(LOG_TAG, "Failed to login.", exception); + + final String message; + + // UserNotConfirmedException will only happen once in the sign-in flow in the case + // that the user attempting to sign in had not confirmed their account by entering + // the correct verification code. A different exception is thrown if the code + // is invalid, so this will not create an continuous confirmation loop if the + // user enters the wrong code. + if (exception instanceof UserNotConfirmedException) { + resendConfirmationCode(); + return; + } + + if (exception instanceof UserNotFoundException) { + message = activity.getString(R.string.user_does_not_exist); + } else if (exception instanceof NotAuthorizedException) { + message = activity.getString(R.string.incorrect_username_or_password); + } else { + message = getErrorMessageFromException(exception); + } + + + if (null != resultsHandler) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_in), + activity.getString(R.string.login_failed) + " " + message); + resultsHandler.onError(CognitoUserPoolsSignInProvider.this, exception); + } + } + }; + + /** {@inheritDoc} */ + @Override + public void initialize(final Context context, final AWSConfiguration awsConfiguration) { + this.context = context; + this.awsConfiguration = awsConfiguration; + + Log.d(LOG_TAG, "initalizing Cognito User Pools"); + + final String regionString = getCognitoUserPoolRegion(); + final Regions region = Regions.fromName(regionString); + + this.cognitoUserPool = new CognitoUserPool(context, + getCognitoUserPoolId(), + getCognitoUserPoolClientId(), + getCognitoUserPoolClientSecret(), + region); + + cognitoLoginKey = "cognito-idp." + region.getName() + + ".amazonaws.com/" + getCognitoUserPoolId(); + Log.d(LOG_TAG, "CognitoLoginKey: " + cognitoLoginKey); + } + + /** {@inheritDoc} */ + @Override + public boolean isRequestCodeOurs(final int requestCode) { + return REQUEST_CODES.contains(requestCode); + } + + /** {@inheritDoc} */ + @Override + public void handleActivityResult(final int requestCode, + final int resultCode, + final Intent data) { + + if (Activity.RESULT_OK == resultCode) { + switch (requestCode) { + case FORGOT_PASSWORD_REQUEST_CODE: + password = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.PASSWORD); + verificationCode = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.VERIFICATION_CODE); + + if (password.length() < PASSWORD_MIN_LENGTH) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_forgot_password), + activity.getString(R.string.password_change_failed) + + " " + activity.getString(R.string.password_length_validation_failed)); + return; + } + + Log.d(LOG_TAG, "verificationCode = " + verificationCode); + + forgotPasswordContinuation.setPassword(password); + forgotPasswordContinuation.setVerificationCode(verificationCode); + forgotPasswordContinuation.continueTask(); + break; + case SIGN_UP_REQUEST_CODE: + username = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.USERNAME); + password = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.PASSWORD); + final String givenName = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.GIVEN_NAME); + final String email = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.EMAIL_ADDRESS); + final String phone = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.PHONE_NUMBER); + + if (username.length() < 1) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up), + activity.getString(R.string.sign_up_failed) + + " " + activity.getString(R.string.sign_up_username_missing)); + return; + } + + if (password.length() < PASSWORD_MIN_LENGTH) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up), + activity.getString(R.string.sign_up_failed) + + " " + activity.getString(R.string.password_length_validation_failed)); + return; + } + + Log.d(LOG_TAG, "username = " + username); + Log.d(LOG_TAG, "given_name = " + givenName); + Log.d(LOG_TAG, "email = " + email); + Log.d(LOG_TAG, "phone = " + phone); + + final CognitoUserAttributes userAttributes = new CognitoUserAttributes(); + userAttributes.addAttribute(CognitoUserPoolsSignInProvider.AttributeKeys.GIVEN_NAME, givenName); + userAttributes.addAttribute(CognitoUserPoolsSignInProvider.AttributeKeys.EMAIL_ADDRESS, email); + + if (null != phone && phone.length() > 0) { + userAttributes.addAttribute(CognitoUserPoolsSignInProvider.AttributeKeys.PHONE_NUMBER, phone); + } + + cognitoUserPool.signUpInBackground(username, password, userAttributes, + null, signUpHandler); + + break; + case MFA_REQUEST_CODE: + verificationCode = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.VERIFICATION_CODE); + + if (verificationCode.length() < 1) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_mfa), + activity.getString(R.string.mfa_failed) + + " " + activity.getString(R.string.mfa_code_empty)); + return; + } + + Log.d(LOG_TAG, "verificationCode = " + verificationCode); + + multiFactorAuthenticationContinuation.setMfaCode(verificationCode); + multiFactorAuthenticationContinuation.continueTask(); + + break; + case VERIFICATION_REQUEST_CODE: + username = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.USERNAME); + verificationCode = data.getStringExtra(CognitoUserPoolsSignInProvider.AttributeKeys.VERIFICATION_CODE); + + if (username.length() < 1) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up_confirm), + activity.getString(R.string.sign_up_confirm_title) + + " " + activity.getString(R.string.sign_up_username_missing)); + return; + } + + if (verificationCode.length() < 1) { + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_up_confirm), + activity.getString(R.string.sign_up_confirm_title) + + " " + activity.getString(R.string.sign_up_confirm_code_missing)); + return; + } + + Log.d(LOG_TAG, "username = " + username); + Log.d(LOG_TAG, "verificationCode = " + verificationCode); + + final CognitoUser cognitoUser = cognitoUserPool.getUser(username); + + cognitoUser.confirmSignUpInBackground(verificationCode, true, signUpConfirmationHandler); + + break; + default: + Log.e(LOG_TAG, "Unknown Request Code sent."); + } + } + + } + + /** {@inheritDoc} */ + @Override + public View.OnClickListener initializeSignInButton(final Activity signInActivity, + final View buttonView, + final SignInProviderResultHandler providerResultsHandler) { + this.activity = signInActivity; + this.resultsHandler = providerResultsHandler; + + final UserPoolSignInView userPoolSignInView = + (UserPoolSignInView) activity.findViewById(R.id.user_pool_sign_in_view_id); + + this.backgroundColor = userPoolSignInView.getBackgroundColor(); + + userPoolSignInView.getSignUpTextView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(context, SignUpActivity.class); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + CognitoUserPoolsSignInProvider.this.backgroundColor); + activity.startActivityForResult(intent, CognitoUserPoolsSignInProvider.SIGN_UP_REQUEST_CODE); + } + }); + + final TextView forgotPasswordTextView = userPoolSignInView.getForgotPasswordTextView(); + forgotPasswordTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View view) { + username = userPoolSignInView.getEnteredUserName(); + if (username.length() < 1) { + Log.w(LOG_TAG, "Missing username."); + ViewHelper.showDialog(activity, activity.getString(R.string.title_activity_sign_in), "Missing username."); + } else { + final CognitoUser cognitoUser = cognitoUserPool.getUser(username); + + cognitoUser.forgotPasswordInBackground(forgotPasswordHandler); + } + } + }); + + final View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(final View view) { + username = userPoolSignInView.getEnteredUserName(); + password = userPoolSignInView.getEnteredPassword(); + + final CognitoUser cognitoUser = cognitoUserPool.getUser(username); + + cognitoUser.getSessionInBackground(authenticationHandler); + } + }; + + buttonView.setOnClickListener(listener); + return listener; + } + + /** {@inheritDoc} */ + @Override + public String getDisplayName() { + return "Amazon Cognito Your User Pools"; + } + + /** {@inheritDoc} */ + @Override + public String getCognitoLoginKey() { + return cognitoLoginKey; + } + + /** + * Authentication handler for handling token refresh. + */ + private static class RefreshSessionAuthenticationHandler implements AuthenticationHandler { + private CognitoUserSession userSession = null; + + private CognitoUserSession getUserSession() { + return userSession; + } + + @Override + public void onSuccess(final CognitoUserSession userSession, final CognitoDevice newDevice) { + this.userSession = userSession; + } + + @Override + public void getAuthenticationDetails(final AuthenticationContinuation authenticationContinuation, + final String UserId) { + Log.d(LOG_TAG, "Can't refresh the session silently, due to authentication details needed."); + } + + @Override + public void getMFACode(final MultiFactorAuthenticationContinuation continuation) { + Log.wtf(LOG_TAG, "Refresh flow can not trigger request for MFA code."); + } + + @Override + public void authenticationChallenge(final ChallengeContinuation continuation) { + Log.wtf(LOG_TAG, "Refresh flow can not trigger request for authentication challenge."); + } + + @Override + public void onFailure(final Exception exception) { + Log.e(LOG_TAG, "Can't refresh session.", exception); + } + } + + /** {@inheritDoc} */ + @Override + public boolean refreshUserSignInState() { + if (null != cognitoUserSession && cognitoUserSession.isValid()) { + return true; + } + + final RefreshSessionAuthenticationHandler refreshSessionAuthenticationHandler + = new RefreshSessionAuthenticationHandler(); + + cognitoUserPool.getCurrentUser().getSession(refreshSessionAuthenticationHandler); + if (null != refreshSessionAuthenticationHandler.getUserSession()) { + cognitoUserSession = refreshSessionAuthenticationHandler.getUserSession(); + Log.i(LOG_TAG, "refreshUserSignInState: Signed in with Cognito."); + return true; + } + + Log.i(LOG_TAG, "refreshUserSignInState: Not signed in with Cognito."); + cognitoUserSession = null; + return false; + } + + /** {@inheritDoc} */ + @Override + public String getToken() { + return null == cognitoUserSession ? null : cognitoUserSession.getIdToken().getJWTToken(); + } + + /** {@inheritDoc} */ + @Override + public String refreshToken() { + // If there is a session, but the credentials are expired rendering the session not valid. + if ((cognitoUserSession != null) && !cognitoUserSession.isValid()) { + // Attempt to refresh the credentials. + final RefreshSessionAuthenticationHandler refreshSessionAuthenticationHandler + = new RefreshSessionAuthenticationHandler(); + + // Cognito User Pools SDK will attempt to refresh the token upon calling getSession(). + cognitoUserPool.getCurrentUser().getSession(refreshSessionAuthenticationHandler); + + if (null != refreshSessionAuthenticationHandler.getUserSession()) { + cognitoUserSession = refreshSessionAuthenticationHandler.getUserSession(); + } else { + Log.e(LOG_TAG, "Could not refresh the Cognito User Pool Token."); + } + } + + return getToken(); + } + + /** {@inheritDoc} */ + @Override + public void signOut() { + if (null != cognitoUserPool && null != cognitoUserPool.getCurrentUser()) { + cognitoUserPool.getCurrentUser().signOut(); + + cognitoUserSession = null; + username = null; + password = null; + } + } + + /** + * @return the Cognito User Pool. + */ + public CognitoUserPool getCognitoUserPool() { + return cognitoUserPool; + } + + /** + * Retrieve the Cognito UserPool Id from CognitoUserPool -> PoolId key + * + * @throws IllegalArgumentException + * @return CognitoUserPoolId + */ + private String getCognitoUserPoolId() throws IllegalArgumentException { + try { + return this.awsConfiguration.optJsonObject("CognitoUserPool").getString("PoolId"); + } catch (Exception exception) { + throw new IllegalArgumentException( + "Cannot find the PoolId from the " + AWS_CONFIGURATION_FILE + " file.", exception); + } + } + + /** + * Retrieve the Cognito UserPool Id from CognitoUserPool -> AppClientId key + * + * @return CognitoUserPoolId + * @throws IllegalArgumentException + */ + private String getCognitoUserPoolClientId() throws IllegalArgumentException { + try { + return this.awsConfiguration.optJsonObject("CognitoUserPool").getString("AppClientId"); + } catch (Exception exception) { + throw new IllegalArgumentException( + "Cannot find the CognitoUserPool AppClientId from the " + AWS_CONFIGURATION_FILE + " file.", exception); + } + } + + /** + * Retrieve the Cognito UserPool ClientSecret from CognitoUserPool -> AppClientSecret key + * + * @return CognitoUserPoolAppClientSecret + * @throws IllegalArgumentException + */ + private String getCognitoUserPoolClientSecret() throws IllegalArgumentException { + try { + return this.awsConfiguration.optJsonObject("CognitoUserPool").getString("AppClientSecret"); + } catch (Exception exception) { + throw new IllegalArgumentException( + "Cannot find the CognitoUserPool AppClientSecret from the " + AWS_CONFIGURATION_FILE + " file.", exception); + } + } + + /** + * Retrieve the Cognito Region from CognitoUserPool -> Region key + * + * @return CognitoUserPool Region + * @throws IllegalArgumentException + */ + private String getCognitoUserPoolRegion() throws IllegalArgumentException { + try { + return this.awsConfiguration.optJsonObject("CognitoUserPool").getString("Region"); + } catch (Exception exception) { + throw new IllegalArgumentException("Cannot find the CognitoUserPool Region from the " + + AWS_CONFIGURATION_FILE + " file.", exception); + } + } + + /** + * Extract the error message from the exception object. + * @param exception The exception object thrown by Cognito IdentityProvider. + * */ + private static String getErrorMessageFromException(final Exception exception) { + final String message = exception.getLocalizedMessage(); + if (message == null) { + return exception.getMessage(); + } + final int index = message.indexOf(USERPOOLS_EXCEPTION_PREFIX); + if (index == -1) { + return message; + } else { + return message.substring(0, index); + } + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordActivity.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordActivity.java new file mode 100644 index 00000000000..c5fe692dd6a --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordActivity.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +/** + * Activity to prompt for a new password along with the verification code. + */ +public class ForgotPasswordActivity extends Activity { + /** Log tag. */ + private static final String LOG_TAG = ForgotPasswordActivity.class.getSimpleName(); + + private ForgotPasswordView forgotPasswordView; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_forgot_password); + forgotPasswordView = (ForgotPasswordView) findViewById(R.id.forgot_password_view); + } + + /** + * Retrieve input and return to caller. + * @param view the Android View + */ + public void forgotPassword(final View view) { + final String password = forgotPasswordView.getPassword(); + final String verificationCode = forgotPasswordView.getVerificationCode(); + + Log.d(LOG_TAG, "verificationCode = " + verificationCode); + + final Intent intent = new Intent(); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.PASSWORD, password); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.VERIFICATION_CODE, verificationCode); + + setResult(RESULT_OK, intent); + + finish(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordView.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordView.java new file mode 100644 index 00000000000..a10503f96d6 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/ForgotPasswordView.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.support.annotation.Nullable; +import android.text.InputType; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.mobile.auth.core.IdentityManager; +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils; +import com.amazonaws.mobile.auth.core.signin.ui.SplitBackgroundDrawable; + +import com.amazonaws.mobile.auth.userpools.R; + +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_COLOR; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_CORNER_RADIUS; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_SIDE_MARGIN_RATIO; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.MAX_FORM_WIDTH_IN_PIXELS; + +/** + * This view present the ForgotPassword screen for the user to reset the + * password. + */ +public class ForgotPasswordView extends LinearLayout { + private FormView forgotPassForm; + private EditText verificationCodeEditText; + private EditText passwordEditText; + + private SplitBackgroundDrawable splitBackgroundDrawable; + + /** + * Constructs the ForgotPassword View. + * @param context The activity context. + */ + public ForgotPasswordView(final Context context) { + this(context, null); + } + + /** + * Constructs the ForgotPassword View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + */ + public ForgotPasswordView(final Context context, @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructs the ForgotPassword View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + * @param defStyleAttr The resource identifier for the default style attribute. + */ + public ForgotPasswordView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOrientation(VERTICAL); + + final int backgroundColor; + if (isInEditMode()) { + backgroundColor = Color.DKGRAY; + } else { + final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.ForgotPasswordView); + backgroundColor = styledAttributes.getInt(R.styleable.ForgotPasswordView_forgotPasswordViewBackgroundColor, + Color.DKGRAY); + styledAttributes.recycle(); + } + + splitBackgroundDrawable = new SplitBackgroundDrawable(0, getBackgroundColor(context, backgroundColor)); + } + + private int getBackgroundColor(final Context context, final int defaultBackgroundColor) { + Intent intent = ((Activity) context).getIntent(); + return (int) (intent.getIntExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + defaultBackgroundColor)); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + forgotPassForm = (FormView) findViewById(R.id.forgot_password_form); + + verificationCodeEditText = forgotPassForm.addFormField(getContext(), + InputType.TYPE_CLASS_NUMBER, + getContext().getString(R.string.sign_up_confirm_code)); + + passwordEditText = forgotPassForm.addFormField(getContext(), + InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD, + getContext().getString(R.string.sign_in_password)); + + setupConfirmButtonColor(); + } + + private void setupConfirmButtonColor() { + final Button confirmButton = (Button) findViewById(R.id.forgot_password_button); + confirmButton.setBackgroundDrawable(DisplayUtils.getRoundedRectangleBackground( + FORM_BUTTON_CORNER_RADIUS, FORM_BUTTON_COLOR)); + LayoutParams signUpButtonLayoutParams = (LayoutParams) confirmButton.getLayoutParams(); + signUpButtonLayoutParams.setMargins( + forgotPassForm.getFormShadowMargin(), + signUpButtonLayoutParams.topMargin, + forgotPassForm.getFormShadowMargin(), + signUpButtonLayoutParams.bottomMargin); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + final int maxWidth = Math.min((int)(parentWidth * FORM_SIDE_MARGIN_RATIO), MAX_FORM_WIDTH_IN_PIXELS); + super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setupSplitBackground(); + } + + private void setupSplitBackground() { + splitBackgroundDrawable.setSplitPointDistanceFromTop(forgotPassForm.getTop() + + (forgotPassForm.getMeasuredHeight()/2)); + ((ViewGroup) getParent()).setBackgroundDrawable(splitBackgroundDrawable); + } + + public String getVerificationCode() { + return verificationCodeEditText.getText().toString(); + } + + public String getPassword() { + return passwordEditText.getText().toString(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormEditText.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormEditText.java new file mode 100644 index 00000000000..f3c2b138e46 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormEditText.java @@ -0,0 +1,184 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.content.Context; +import android.graphics.Color; +import android.support.annotation.IdRes; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.amazonaws.mobile.auth.userpools.R; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; + +import java.util.Locale; + +/** + * An EditText that shows the hint as a floating label once text is entered in the view. + */ +public class FormEditText extends LinearLayout { + private static final int TEXT_VIEW_ID = 0xF01; + private static final int EDIT_TEXT_ID = 0xF02; + private static final int BIT_FOR_SHOWING_PASSWORD = InputType.TYPE_TEXT_VARIATION_PASSWORD ^ InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + private static final int TEXT_VIEW_TOP_MARGIN = dp(5); + private static final int EDIT_VIEW_BOTTOM_PADDING = dp(5); + private static final int SHOW_PASSWORD_LEFT_RIGHT_MARGIN = dp(5); + private static final int SHOW_PASSWORD_TOP_MARGIN = dp(-5); // float the show box up. + private TextView textView; + private EditText editText; + private LinearLayout editFieldLayout; + private TextView showPasswordToggleTextView = null; + private boolean hasSetMinimumSize = false; + + @IdRes + private int toViewId(int id) { + return id; + } + + public FormEditText(Context context, int inputType, final String fieldName) { + super(context); + this.setOrientation(VERTICAL); + this.setGravity(Gravity.CENTER_VERTICAL); + + textView = new TextView(context); + textView.setText(fieldName.toUpperCase(Locale.getDefault())); + textView.setId(toViewId(TEXT_VIEW_ID)); + final LinearLayout.LayoutParams textViewLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + textViewLayoutParams.setMargins(0, TEXT_VIEW_TOP_MARGIN, 0, 0); + this.addView(textView, textViewLayoutParams); + textView.setVisibility(INVISIBLE); + + editText = new EditText(context); + editText.setSingleLine(); + editText.setInputType(inputType); + editText.setBackgroundColor(Color.TRANSPARENT); + editText.setPadding(0,dp(2), 0, dp(2) + EDIT_VIEW_BOTTOM_PADDING); + editText.setId(toViewId(EDIT_TEXT_ID)); + editText.setHint(fieldName); + + final LinearLayout.LayoutParams editTextLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + editTextLayoutParams.setMargins(0, 0, 0, 0); + + if ((inputType & InputType.TYPE_TEXT_VARIATION_PASSWORD) > 0) { + editFieldLayout = new LinearLayout(context); + editFieldLayout.setOrientation(HORIZONTAL); + editTextLayoutParams.gravity = Gravity.START; + editTextLayoutParams.weight = 1; + editFieldLayout.addView(editText, editTextLayoutParams); + + showPasswordToggleTextView = new TextView(context); + setupShowHidePassword(); + this.addView(editFieldLayout); + + } else { + this.addView(editText, editTextLayoutParams); + } + + setupTextChangedListener(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (!hasSetMinimumSize) { + this.setMinimumHeight(textView.getMeasuredHeight() + TEXT_VIEW_TOP_MARGIN + editText.getMeasuredHeight()); + textView.setVisibility(GONE); + hasSetMinimumSize = true; + } + } + + private void setupShowHidePassword() { + final String showText = getResources().getString(R.string.sign_in_show_password); + final String hideText = getResources().getString(R.string.sign_in_hide_password); + + showPasswordToggleTextView.setText(showText); + final LinearLayout.LayoutParams showPassLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + showPassLayoutParams.setMargins( + SHOW_PASSWORD_LEFT_RIGHT_MARGIN, SHOW_PASSWORD_TOP_MARGIN, SHOW_PASSWORD_LEFT_RIGHT_MARGIN, 0); + showPassLayoutParams.gravity = Gravity.END | Gravity.CENTER_VERTICAL; + + editFieldLayout.addView(showPasswordToggleTextView, showPassLayoutParams); + showPasswordToggleTextView.setVisibility(GONE); + + showPasswordToggleTextView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + final CharSequence oldText = showPasswordToggleTextView.getText(); + final CharSequence newText = oldText.equals(showText) ? hideText : showText; + showPasswordToggleTextView.setText(newText); + int selectionStart = editText.getSelectionStart(); + int selectionEnd = editText.getSelectionEnd(); + if (oldText.equals(showText)) { + editText.setInputType(editText.getInputType() | BIT_FOR_SHOWING_PASSWORD); + } else { + editText.setInputType(editText.getInputType() & ~BIT_FOR_SHOWING_PASSWORD); + } + editText.setSelection(selectionStart, selectionEnd); + } + }); + } + + private void setupTextChangedListener() { + editText.addTextChangedListener(new TextWatcher() { + private void handleFloatingTextVisibility() { + if (editText.getText().length() == 0) { + if (hasSetMinimumSize) { + textView.setVisibility(GONE); + } + editText.setPadding(0,dp(2), 0, dp(2) + EDIT_VIEW_BOTTOM_PADDING); + if (showPasswordToggleTextView != null) { + showPasswordToggleTextView.setVisibility(GONE); + } + } else { + textView.setVisibility(VISIBLE); + editText.setPadding(0,dp(1), 0, dp(3) + EDIT_VIEW_BOTTOM_PADDING); + if (showPasswordToggleTextView != null) { + showPasswordToggleTextView.setVisibility(VISIBLE); + } + } + } + + @Override + public void beforeTextChanged(final CharSequence text, final int start, final int count, final int after) { + } + + @Override + public void onTextChanged(final CharSequence text, final int start, final int before, final int count) { + } + + @Override + public void afterTextChanged(final Editable text) { + handleFloatingTextVisibility(); + } + }); + } + + public EditText getEditTextView() { + return editText; + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormView.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormView.java new file mode 100644 index 00000000000..85a3bf4b612 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/FormView.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.getRoundedRectangleBackground; + +/** + * A view for displaying text and passwords. + */ +public class FormView extends LinearLayout { + /** Corner radius. */ + private static final int FORM_CORNER_RADIUS = dp(8); + private static final int FIELD_LEFT_RIGHT_MARGIN = dp(20); + + /** Background Drawables for the form. */ + private final Drawable[] backgroundDrawables = new Drawable[] { + // Border Shadow + createRoundedRectShape(FORM_CORNER_RADIUS, Color.DKGRAY, 10), + createRoundedRectShape(FORM_CORNER_RADIUS, Color.DKGRAY, 20), + createRoundedRectShape(FORM_CORNER_RADIUS, Color.DKGRAY, 30), + createRoundedRectShape(FORM_CORNER_RADIUS, Color.DKGRAY, 50), + createRoundedRectShape(FORM_CORNER_RADIUS, Color.DKGRAY, 80), + // Background Color + createRoundedRectShape(FORM_CORNER_RADIUS, Color.WHITE, 100)}; + + public FormView(final Context context) { + this(context, null); + } + + public FormView(final Context context, @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + public FormView(final Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.setOrientation(VERTICAL); + this.setBackgroundDrawable(getFormBackground()); + } + + private ShapeDrawable createRoundedRectShape(final int cornerRadius, final int color, final int alpha) { + final ShapeDrawable insetBorderDrawable = + getRoundedRectangleBackground(cornerRadius, color); + if (alpha < 100) { + insetBorderDrawable.setAlpha(alpha); + } + insetBorderDrawable.getPaint().setColor(color); + return insetBorderDrawable; + } + + /** + * Create the form background. + * @return the background drawable. + */ + private Drawable getFormBackground() { + final LayerDrawable layerDrawable = new LayerDrawable(backgroundDrawables); + + for (int i = 0; i < backgroundDrawables.length; i++) { + layerDrawable.setLayerInset(i, dp(i), dp(i), dp(i), dp(i)); + } + + return layerDrawable; + } + + private static class Divider extends View { + final Paint paint; + public Divider(Context context) { + super(context); + paint = new Paint(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + paint.setColor(Color.LTGRAY); + canvas.drawRect(getMeasuredWidth() * 0.1f, 0, getMeasuredWidth() * 0.9f, getMeasuredHeight(), paint); + } + } + + private EditText addField(final Context context, int inputType, final String fieldName) { + final int additionalTopMarginForFirstElement; + + if (getChildCount() == 0) { + additionalTopMarginForFirstElement = getFormShadowMargin(); + } else { + additionalTopMarginForFirstElement = 0; + + // Get previous ites layout params. + final LinearLayout.LayoutParams prevFormEditTextLayoutParams + = (LayoutParams)getChildAt(getChildCount() - 1).getLayoutParams(); + + // Clear previous item's bottom margin. + prevFormEditTextLayoutParams.setMargins( + prevFormEditTextLayoutParams.leftMargin, + prevFormEditTextLayoutParams.topMargin, + prevFormEditTextLayoutParams.rightMargin, + 0); + + // Add a divider before the next item. + addView(new Divider(context), LayoutParams.MATCH_PARENT, dp(1)); + } + + final FormEditText formEditText = new FormEditText(context, inputType, fieldName); + final LinearLayout.LayoutParams formEditTextLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + + formEditTextLayoutParams.setMargins(FIELD_LEFT_RIGHT_MARGIN, additionalTopMarginForFirstElement, FIELD_LEFT_RIGHT_MARGIN, getFormShadowMargin()); + this.addView(formEditText, formEditTextLayoutParams); + + return formEditText.getEditTextView(); + } + + + /** + * Add a field to the form. + * @param context the context. + * @param inputType the desired EditText input type. + * @param fieldName the field name. + * @return the EditText object created within the form. + */ + public EditText addFormField(final Context context, int inputType, final String fieldName) { + return addField(context, inputType, fieldName); + } + + /** + * @return the number of margin pixels drawn on each side of the form. + */ + public int getFormShadowMargin() { + return dp(backgroundDrawables.length - 1); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAActivity.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAActivity.java new file mode 100644 index 00000000000..a32fa703637 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAActivity.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import com.amazonaws.mobile.auth.userpools.R; + +/** + * Activity to prompt for a a verification code. + */ +public class MFAActivity extends Activity { + /** Log tag. */ + private static final String LOG_TAG = MFAActivity.class.getSimpleName(); + private MFAView mfaView; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_mfa); + mfaView = (MFAView) findViewById(R.id.mfa_view); + } + + /** + * Retrieve input and return to caller. + * @param view the Android View + */ + public void verify(final View view) { + final String verificationCode = mfaView.getMFACode(); + + Log.d(LOG_TAG, "verificationCode = " + verificationCode); + + final Intent intent = new Intent(); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.VERIFICATION_CODE, verificationCode); + + setResult(RESULT_OK, intent); + + finish(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAView.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAView.java new file mode 100644 index 00000000000..94099cc6e6c --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/MFAView.java @@ -0,0 +1,145 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.support.annotation.Nullable; +import android.text.InputType; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.mobile.auth.core.IdentityManager; +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils; +import com.amazonaws.mobile.auth.core.signin.ui.SplitBackgroundDrawable; + +import com.amazonaws.mobile.auth.userpools.R; + +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_COLOR; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_CORNER_RADIUS; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_SIDE_MARGIN_RATIO; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.MAX_FORM_WIDTH_IN_PIXELS; + +/** + * View for showing MFA confirmation upon sign-in. + */ +public class MFAView extends LinearLayout { + private FormView mfaForm; + private EditText mfaCodeEditText; + private SplitBackgroundDrawable splitBackgroundDrawable; + + /** + * Constructs the MFA View. + * @param context The activity context. + */ + public MFAView(Context context) { + this(context, null); + } + + /** + * Constructs the MFA View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + */ + public MFAView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructs the MFA View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + * @param defStyleAttr The resource identifier for the default style attribute. + */ + public MFAView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOrientation(VERTICAL); + final int backgroundColor; + if (isInEditMode()) { + backgroundColor = Color.DKGRAY; + } else { + final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.MFAView); + backgroundColor = styledAttributes.getInt(R.styleable.MFAView_mfaViewBackgroundColor, Color.DKGRAY); + styledAttributes.recycle(); + } + + splitBackgroundDrawable = new SplitBackgroundDrawable(0, getBackgroundColor(context, backgroundColor)); + } + + private int getBackgroundColor(final Context context, final int defaultBackgroundColor) { + Intent intent = ((Activity) context).getIntent(); + return (int) (intent.getIntExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + defaultBackgroundColor)); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mfaForm = (FormView) findViewById(R.id.mfa_form); + + mfaCodeEditText = mfaForm.addFormField(getContext(), + InputType.TYPE_CLASS_NUMBER, + getContext().getString(R.string.forgot_password_input_code_hint)); + + setupVerifyButtonColor(); + } + + private void setupVerifyButtonColor() { + final Button confirmButton = (Button) findViewById(R.id.mfa_button); + confirmButton.setBackgroundDrawable( + DisplayUtils.getRoundedRectangleBackground(FORM_BUTTON_CORNER_RADIUS, FORM_BUTTON_COLOR)); + final LayoutParams signUpButtonLayoutParams = (LayoutParams) confirmButton.getLayoutParams(); + signUpButtonLayoutParams.setMargins( + mfaForm.getFormShadowMargin(), + signUpButtonLayoutParams.topMargin, + mfaForm.getFormShadowMargin(), + signUpButtonLayoutParams.bottomMargin); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + final int maxWidth = Math.min((int)(parentWidth * FORM_SIDE_MARGIN_RATIO), MAX_FORM_WIDTH_IN_PIXELS); + super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setupSplitBackground(); + } + + private void setupSplitBackground() { + splitBackgroundDrawable.setSplitPointDistanceFromTop(mfaForm.getTop() + + (mfaForm.getMeasuredHeight()/2)); + ((ViewGroup)getParent()).setBackgroundDrawable(splitBackgroundDrawable); + } + + public String getMFACode() { + return mfaCodeEditText.getText().toString(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpActivity.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpActivity.java new file mode 100644 index 00000000000..48e4451c6c9 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpActivity.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import com.amazonaws.mobile.auth.userpools.R; + +/** + * Activity to prompt for account sign up information. + */ +public class SignUpActivity extends Activity { + /** Log tag. */ + private static final String LOG_TAG = SignUpActivity.class.getSimpleName(); + + private SignUpView signUpView; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sign_up); + + signUpView = (SignUpView) findViewById(R.id.signup_view); + + } + + @Override + protected void onResume() { + super.onResume(); + } + + /** + * Retrieve input and return to caller. + * @param view the Android View + */ + public void signUp(final View view) { + + final String username = signUpView.getUserName(); + final String password = signUpView.getPassword(); + final String givenName = signUpView.getGivenName(); + final String email = signUpView.getEmail(); + final String phone = signUpView.getPhone(); + + Log.d(LOG_TAG, "username = " + username); + Log.d(LOG_TAG, "given_name = " + givenName); + Log.d(LOG_TAG, "email = " + email); + Log.d(LOG_TAG, "phone = " + phone); + + final Intent intent = new Intent(); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.USERNAME, username); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.PASSWORD, password); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.GIVEN_NAME, givenName); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.EMAIL_ADDRESS, email); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.PHONE_NUMBER, phone); + + setResult(RESULT_OK, intent); + + finish(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmActivity.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmActivity.java new file mode 100644 index 00000000000..0a74ef6ab76 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmActivity.java @@ -0,0 +1,71 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import com.amazonaws.mobile.auth.userpools.R; + +/** + * Activity to prompt for sign-up confirmation information. + */ +public class SignUpConfirmActivity extends Activity { + /** Log tag. */ + private static final String LOG_TAG = SignUpConfirmActivity.class.getSimpleName(); + private SignUpConfirmView signUpConfirmView; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sign_up_confirm); + + final String username = getIntent().getStringExtra( + CognitoUserPoolsSignInProvider.AttributeKeys.USERNAME); + + signUpConfirmView = (SignUpConfirmView) findViewById(R.id.signup_confirm_view); + signUpConfirmView.getUserNameEditText().setText(username); + signUpConfirmView.getConfirmCodeEditText().requestFocus(); + } + + /** + * Retrieve input and return to caller. + * @param view the Android View + */ + public void confirmAccount(final View view) { + + final String username = + signUpConfirmView.getUserNameEditText().getText().toString(); + final String verificationCode = + signUpConfirmView.getConfirmCodeEditText().getText().toString(); + + Log.d(LOG_TAG, "username = " + username); + Log.d(LOG_TAG, "verificationCode = " + verificationCode); + + final Intent intent = new Intent(); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.USERNAME, username); + intent.putExtra(CognitoUserPoolsSignInProvider.AttributeKeys.VERIFICATION_CODE, verificationCode); + + setResult(RESULT_OK, intent); + + finish(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmView.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmView.java new file mode 100644 index 00000000000..751148c6ffd --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpConfirmView.java @@ -0,0 +1,154 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.support.annotation.Nullable; +import android.text.InputType; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.amazonaws.mobile.config.AWSConfiguration; + +import com.amazonaws.mobile.auth.core.IdentityManager; +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils; +import com.amazonaws.mobile.auth.core.signin.ui.SplitBackgroundDrawable; + +import com.amazonaws.mobile.auth.userpools.R; + +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_COLOR; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_CORNER_RADIUS; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_SIDE_MARGIN_RATIO; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.MAX_FORM_WIDTH_IN_PIXELS; + +/** + * This view presents the confirmation screen for user sign up. + */ +public class SignUpConfirmView extends LinearLayout { + private FormView confirmForm; + private EditText userNameEditText; + private EditText confirmCodeEditText; + private SplitBackgroundDrawable splitBackgroundDrawable; + + /** + * Constructs the SignUpConfirm View. + * @param context The activity context. + */ + public SignUpConfirmView(final Context context) { + this(context, null); + } + + /** + * Constructs the SignUpConfirm View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + */ + public SignUpConfirmView(final Context context, @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructs the SignUpConfirm View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + * @param defStyleAttr The resource identifier for the default style attribute. + */ + public SignUpConfirmView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOrientation(VERTICAL); + final int backgroundColor; + if (isInEditMode()) { + backgroundColor = Color.DKGRAY; + } else { + final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.SignUpConfirmView); + backgroundColor = styledAttributes.getInt(R.styleable.SignUpConfirmView_signUpConfirmViewBackgroundColor, Color.DKGRAY); + styledAttributes.recycle(); + } + + splitBackgroundDrawable = new SplitBackgroundDrawable(0, getBackgroundColor(context, backgroundColor)); + } + + private int getBackgroundColor(final Context context, final int defaultBackgroundColor) { + Intent intent = ((Activity) context).getIntent(); + return (int) (intent.getIntExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + defaultBackgroundColor)); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + confirmForm = (FormView) findViewById(R.id.signup_confirm_form); + userNameEditText = confirmForm.addFormField(getContext(), + InputType.TYPE_CLASS_TEXT| InputType.TYPE_TEXT_VARIATION_PERSON_NAME, + getContext().getString(R.string.username_text)); + + confirmCodeEditText = confirmForm.addFormField(getContext(), + InputType.TYPE_CLASS_NUMBER, + getContext().getString(R.string.sign_up_confirm_code)); + + + setupConfirmButtonColor(); + } + + private void setupConfirmButtonColor() { + final Button confirmButton = (Button) findViewById(R.id.confirm_account_button); + confirmButton.setBackgroundDrawable(DisplayUtils.getRoundedRectangleBackground( + FORM_BUTTON_CORNER_RADIUS, FORM_BUTTON_COLOR)); + final LayoutParams signUpButtonLayoutParams = (LayoutParams) confirmButton.getLayoutParams(); + signUpButtonLayoutParams.setMargins( + confirmForm.getFormShadowMargin(), + signUpButtonLayoutParams.topMargin, + confirmForm.getFormShadowMargin(), + signUpButtonLayoutParams.bottomMargin); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + final int maxWidth = Math.min((int)(parentWidth * FORM_SIDE_MARGIN_RATIO), MAX_FORM_WIDTH_IN_PIXELS); + super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setupSplitBackground(); + } + + private void setupSplitBackground() { + splitBackgroundDrawable.setSplitPointDistanceFromTop(confirmForm.getTop() + + (confirmForm.getMeasuredHeight()/2)); + ((ViewGroup)getParent()).setBackgroundDrawable(splitBackgroundDrawable); + } + + public EditText getUserNameEditText() { + return userNameEditText; + } + + public EditText getConfirmCodeEditText() { + return confirmCodeEditText; + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpView.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpView.java new file mode 100644 index 00000000000..2ec6f8e1b69 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/SignUpView.java @@ -0,0 +1,198 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.text.InputType; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.amazonaws.mobile.config.AWSConfiguration; +import com.amazonaws.mobile.auth.core.IdentityManager; +import com.amazonaws.mobile.auth.core.signin.SignInManager; +import com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils; +import com.amazonaws.mobile.auth.core.signin.ui.SplitBackgroundDrawable; + +import com.amazonaws.mobile.auth.userpools.R; + +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_COLOR; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_CORNER_RADIUS; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_SIDE_MARGIN_RATIO; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.MAX_FORM_WIDTH_IN_PIXELS; + +/** + * The view that handles user sign-up for Cognito User Pools. + */ +public class SignUpView extends LinearLayout { + private TextView signUpMessage; + private Button signUpButton; + private FormView signUpForm; + private EditText userNameEditText; + private EditText passwordEditText; + private EditText givenNameEditText; + private EditText emailEditText; + private EditText phoneEditText; + private SplitBackgroundDrawable splitBackgroundDrawable; + + /** + * Constructs the SignUp View. + * @param context The activity context. + */ + public SignUpView(final Context context) { + this(context, null); + } + + /** + * Constructs the SignUp View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + */ + public SignUpView(final Context context, final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructs the SignUp View. + * @param context The activity context. + * @param attrs The Attribute Set for the view from which the resources can be accessed. + * @param defStyleAttr The resource identifier for the default style attribute. + */ + public SignUpView(final Context context, final AttributeSet attrs, final int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOrientation(VERTICAL); + + final int backgroundColor; + if (isInEditMode()) { + backgroundColor = Color.DKGRAY; + } else { + final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.SignUpView); + backgroundColor = styledAttributes.getInt(R.styleable.SignUpView_signUpViewBackgroundColor, Color.DKGRAY); + styledAttributes.recycle(); + } + + splitBackgroundDrawable = new SplitBackgroundDrawable(0, getBackgroundColor(context, backgroundColor)); + } + + private int getBackgroundColor(final Context context, final int defaultBackgroundColor) { + Intent intent = ((Activity) context).getIntent(); + return (int) (intent.getIntExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + defaultBackgroundColor)); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + this.signUpForm = (FormView) findViewById(R.id.signup_form); + userNameEditText = signUpForm.addFormField(getContext(), + InputType.TYPE_CLASS_TEXT| InputType.TYPE_TEXT_VARIATION_PERSON_NAME, + getContext().getString(R.string.username_text)); + + passwordEditText = signUpForm.addFormField(getContext(), + InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD, + getContext().getString(R.string.sign_in_password)); + + givenNameEditText = signUpForm.addFormField(getContext(), + InputType.TYPE_CLASS_TEXT| InputType.TYPE_TEXT_VARIATION_PERSON_NAME, + getContext().getString(R.string.given_name_text)); + + emailEditText = signUpForm.addFormField(getContext(), + InputType.TYPE_CLASS_TEXT| InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, + getContext().getString(R.string.email_address_text)); + + phoneEditText = signUpForm.addFormField(getContext(), + InputType.TYPE_CLASS_PHONE, + getContext().getString(R.string.phone_number_text)); + + this.signUpMessage = (TextView) findViewById(R.id.signup_message); + this.signUpButton = (Button) findViewById(R.id.signup_button); + setupSignUpButtonBackground(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + final int maxWidth = Math.min((int)(parentWidth * FORM_SIDE_MARGIN_RATIO), MAX_FORM_WIDTH_IN_PIXELS); + super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setupSplitBackground(); + } + + private void setupSignUpButtonBackground() { + signUpButton.setBackgroundDrawable(DisplayUtils.getRoundedRectangleBackground( + FORM_BUTTON_CORNER_RADIUS, FORM_BUTTON_COLOR)); + LayoutParams signUpButtonLayoutParams = (LayoutParams) signUpButton.getLayoutParams(); + signUpButtonLayoutParams.setMargins( + signUpForm.getFormShadowMargin(), + signUpButtonLayoutParams.topMargin, + signUpForm.getFormShadowMargin(), + signUpButtonLayoutParams.bottomMargin); + } + + private void setupSplitBackground() { + splitBackgroundDrawable.setSplitPointDistanceFromTop( + signUpForm.getTop() + (signUpForm.getMeasuredHeight()/2)); + ((ViewGroup) getParent()).setBackgroundDrawable(splitBackgroundDrawable); + } + + /** + * @return the user's user name entered in the form. + */ + public String getUserName() { + return userNameEditText.getText().toString(); + } + + /** + * @return the user's password entered in the form. + */ + public String getPassword() { + return passwordEditText.getText().toString(); + } + + /** + * @return the user's given name entered in the form. + */ + public String getGivenName() { + return givenNameEditText.getText().toString(); + } + + /** + * @return the user's email entered in the form. + */ + public String getEmail() { + return emailEditText.getText().toString(); + } + + /** + * @return the user's phone number entered in the form. + */ + public String getPhone() { + return phoneEditText.getText().toString(); + } +} diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolFormConstants.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolFormConstants.java new file mode 100644 index 00000000000..8023f21bdb8 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolFormConstants.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; + +/** + * Common constants for user pool forms. + */ +public final class UserPoolFormConstants { + /** Form Button Color. */ + public static final int FORM_BUTTON_COLOR = 0xff4599ff; + + /** Form button radius in pixels. */ + public static final int FORM_BUTTON_CORNER_RADIUS = dp(5); + + /** Ratio for the form size relative to the parent view. */ + public static final double FORM_SIDE_MARGIN_RATIO = 0.85; + + /** Maximum width of the form in pixels. */ + public static final int MAX_FORM_WIDTH_IN_PIXELS = dp(300); +} \ No newline at end of file diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolSignInView.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolSignInView.java new file mode 100644 index 00000000000..b4534a875e1 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/UserPoolSignInView.java @@ -0,0 +1,238 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazonaws.mobile.auth.userpools; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Color; +import android.support.annotation.Nullable; +import android.text.InputType; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; + +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.amazonaws.mobile.auth.core.signin.SignInManager; + +import com.amazonaws.mobile.auth.userpools.R; + +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.dp; +import static com.amazonaws.mobile.auth.core.signin.ui.DisplayUtils.getRoundedRectangleBackground; + +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_COLOR; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_BUTTON_CORNER_RADIUS; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.FORM_SIDE_MARGIN_RATIO; +import static com.amazonaws.mobile.auth.userpools.UserPoolFormConstants.MAX_FORM_WIDTH_IN_PIXELS; + +/** + * User Pools Sign-in Control. This view presents a form to handle user sign-in. + * It also presents choices for creating a new account or retrieving a forgotten password. + */ +public class UserPoolSignInView extends LinearLayout { + + /** Log tag. */ + private static final String LOG_TAG = UserPoolSignInView.class.getSimpleName(); + + /** Create Account Text View */ + private TextView signUpTextView; + + /** Forgot Password Text View */ + private TextView forgotPasswordTextView; + + /** The credentials form that styles the username and password fields. */ + private FormView credentialsFormView; + + /** The Username field. */ + private EditText userNameEditText; + + /** The Password field. */ + private EditText passwordEditText; + + /** The sign in button. */ + private Button signInButton; + + /** Flag for whether the control has been intitialized. */ + private boolean isInitialized; + + /** Background Color. */ + private int backgroundColor; + + /** Default Background color used by the views. */ + private static final int DEFAULT_BACKGROUND_COLOR = Color.DKGRAY; + + public UserPoolSignInView(final Context context) { + this(context, null); + } + + public UserPoolSignInView(final Context context, @Nullable final AttributeSet attrs) { + this(context, attrs, 0); + } + + public UserPoolSignInView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.setOrientation(VERTICAL); + this.setGravity(Gravity.CENTER); + this.setId(R.id.user_pool_sign_in_view_id); + + setupCredentialsForm(context); + setupSignInButton(context); + setupLayoutForSignUpAndForgotPassword(context); + setupBackgroundColor(context); + } + + private void initializeIfNecessary() { + if (isInitialized) { + return; + } + isInitialized = true; + + if (isInEditMode()) { + return; + } + + try { + final SignInManager signInManager = SignInManager.getInstance(); + signInManager.initializeSignInButton(CognitoUserPoolsSignInProvider.class, signInButton); + } catch (final Exception exception) { + Log.e(LOG_TAG, "Cannot initialize the SignInButton. Please check if IdentityManager :" + + " startUpAuth and setUpToAuthenticate are invoked", exception); + } + } + + private void setupCredentialsForm(final Context context) { + credentialsFormView = new FormView(context); + final LinearLayout.LayoutParams formViewLayoutParams + = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + + userNameEditText = credentialsFormView.addFormField(context, + InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, + // User Pools requires sign in with the username or verified channel. + // Mobile Hub does not set up email verification because it requires SES verification. + // Hence, prompt customers to login using the username or phone number. + context.getString(R.string.sign_in_username)); + passwordEditText = credentialsFormView.addFormField(context, + InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD, + context.getString(R.string.sign_in_password)); + + this.addView(credentialsFormView, formViewLayoutParams); + } + + private void setupSignInButton(final Context context) { + signInButton = new Button(context); + signInButton.setTextColor(Color.WHITE); + signInButton.setText(context.getString(R.string.sign_in_button_text)); + signInButton.setAllCaps(false); + signInButton.setBackgroundDrawable( + getRoundedRectangleBackground(FORM_BUTTON_CORNER_RADIUS, FORM_BUTTON_COLOR)); + + final Resources resources = getResources(); + final LinearLayout.LayoutParams signInButtonLayoutParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + resources.getDimensionPixelSize(R.dimen.sign_in_button_height)); + final int signInButtonMarginTopBottom + = resources.getDimensionPixelSize(R.dimen.user_pools_sign_in_button_margin_top_bottom); + signInButtonLayoutParams.setMargins( + credentialsFormView.getFormShadowMargin(), + signInButtonMarginTopBottom + credentialsFormView.getFormShadowMargin(), + credentialsFormView.getFormShadowMargin(), + 0); + this.addView(signInButton, signInButtonLayoutParams); + } + + private void setupLayoutForSignUpAndForgotPassword(final Context context) { + final LinearLayout layoutForSignUpAndForgotPassword = new LinearLayout(context); + layoutForSignUpAndForgotPassword.setOrientation(HORIZONTAL); + final LinearLayout.LayoutParams layoutParamsForSignUpAndForgotPassword + = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + + layoutParamsForSignUpAndForgotPassword.setMargins(credentialsFormView.getFormShadowMargin(), + dp(10), credentialsFormView.getFormShadowMargin(), 0); + layoutParamsForSignUpAndForgotPassword.gravity = Gravity.CENTER_HORIZONTAL; + + signUpTextView = new TextView(context); + signUpTextView.setText(R.string.sign_in_new_account); + signUpTextView.setTextAppearance(context, android.R.style.TextAppearance_Small); + signUpTextView.setGravity(Gravity.START); + signUpTextView.setTextColor(FORM_BUTTON_COLOR); + final LinearLayout.LayoutParams layoutParamsForNewAccountText + = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layoutParamsForNewAccountText.weight = 1; + layoutForSignUpAndForgotPassword.addView(signUpTextView, layoutParamsForNewAccountText); + + forgotPasswordTextView = new TextView(context); + forgotPasswordTextView.setText(R.string.sign_in_forgot_password); + forgotPasswordTextView.setTextAppearance(context, android.R.style.TextAppearance_Small); + forgotPasswordTextView.setGravity(Gravity.END); + forgotPasswordTextView.setTextColor(FORM_BUTTON_COLOR); + final LinearLayout.LayoutParams layoutParamsForForgotPassword + = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layoutParamsForForgotPassword.weight = 1; + layoutForSignUpAndForgotPassword.addView(forgotPasswordTextView, layoutParamsForForgotPassword); + + this.addView(layoutForSignUpAndForgotPassword, layoutParamsForSignUpAndForgotPassword); + } + + private void setupBackgroundColor(final Context context) { + Intent intent = ((Activity) context).getIntent(); + this.backgroundColor = (int) (intent.getIntExtra(CognitoUserPoolsSignInProvider.AttributeKeys.BACKGROUND_COLOR, + DEFAULT_BACKGROUND_COLOR)); + } + + /** + * Gets the Background Color passed in by the UI. + * @return + */ + public int getBackgroundColor() { + return this.backgroundColor; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + final int maxWidth = Math.min((int)(parentWidth * FORM_SIDE_MARGIN_RATIO), MAX_FORM_WIDTH_IN_PIXELS); + super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), heightMeasureSpec); + initializeIfNecessary(); + } + + public TextView getSignUpTextView() { + return signUpTextView; + } + + public TextView getForgotPasswordTextView() { + return forgotPasswordTextView; + } + + public String getEnteredUserName() { + return userNameEditText.getText().toString(); + } + + public String getEnteredPassword() { + return passwordEditText.getText().toString(); + } + + public FormView getCredentialsFormView() { + return credentialsFormView; + } +} \ No newline at end of file diff --git a/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/package-info.java b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/package-info.java new file mode 100644 index 00000000000..01bd0ff1dd4 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/java/com/amazonaws/mobile/auth/userpools/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2013-2017 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Package Info for UserPools. + */ +package com.amazonaws.mobile.auth.userpools; \ No newline at end of file diff --git a/aws-android-sdk-auth-userpools/src/main/res/layout/activity_forgot_password.xml b/aws-android-sdk-auth-userpools/src/main/res/layout/activity_forgot_password.xml new file mode 100644 index 00000000000..a9e47c00427 --- /dev/null +++ b/aws-android-sdk-auth-userpools/src/main/res/layout/activity_forgot_password.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + +