From 3940def90c67b9d89fe356ea1dd7ed29882cedb9 Mon Sep 17 00:00:00 2001 From: Tony Myles Date: Fri, 20 Sep 2019 15:44:54 -0700 Subject: [PATCH 1/2] Fixed AferoClientAPI.resetPasswordWithCode by adding a ResetPasswordBody class that will be serialized into the proper json the service is expecting. AferoLab: Added a basic Forgot Password/Request Code/Reset Password UI flow to the sample app. ## JIRA [ACE-274: HTTP 400 on Android resetPasswordWithCode](https://kibanlabs.atlassian.net/browse/ACE-274) --- .../retrofit2/AferoClientRetrofit2.java | 3 +- .../client/retrofit2/api/AferoClientAPI.java | 3 +- .../afero/models/ResetPasswordBody.java | 14 +++ afero-sdk-softhub/build.gradle | 4 - samples/afero-lab/app/build.gradle | 7 +- .../java/io/afero/aferolab/MainActivity.java | 19 +++- .../resetPassword/RequestCodeController.java | 62 ++++++++++++ .../resetPassword/RequestCodeView.java | 98 +++++++++++++++++++ .../ResetPasswordController.java | 51 ++++++++++ .../resetPassword/ResetPasswordView.java | 97 ++++++++++++++++++ .../io/afero/aferolab/widget/ScreenView.java | 9 ++ .../app/src/main/res/layout/activity_main.xml | 54 ++++++---- .../src/main/res/layout/view_request_code.xml | 76 ++++++++++++++ .../main/res/layout/view_reset_password.xml | 74 ++++++++++++++ .../app/src/main/res/values/strings.xml | 7 ++ 15 files changed, 548 insertions(+), 30 deletions(-) create mode 100644 afero-sdk-core/src/main/java/io/afero/sdk/client/afero/models/ResetPasswordBody.java create mode 100644 samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeController.java create mode 100644 samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeView.java create mode 100644 samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordController.java create mode 100644 samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordView.java create mode 100644 samples/afero-lab/app/src/main/res/layout/view_request_code.xml create mode 100644 samples/afero-lab/app/src/main/res/layout/view_reset_password.xml diff --git a/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/AferoClientRetrofit2.java b/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/AferoClientRetrofit2.java index ab7efd9..4027478 100644 --- a/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/AferoClientRetrofit2.java +++ b/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/AferoClientRetrofit2.java @@ -29,6 +29,7 @@ import io.afero.sdk.client.afero.models.InvitationDetails; import io.afero.sdk.client.afero.models.Location; import io.afero.sdk.client.afero.models.PostActionBody; +import io.afero.sdk.client.afero.models.ResetPasswordBody; import io.afero.sdk.client.afero.models.RuleExecuteBody; import io.afero.sdk.client.afero.models.ViewRequest; import io.afero.sdk.client.afero.models.ViewResponse; @@ -423,7 +424,7 @@ public Observable resetPassword(String email) { */ @Override public Observable resetPasswordWithCode(String resetCode, String newPassword) { - return mAferoService.resetPasswordWithCode(resetCode, newPassword); + return mAferoService.resetPasswordWithCode(resetCode, new ResetPasswordBody(newPassword)); } /** diff --git a/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/api/AferoClientAPI.java b/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/api/AferoClientAPI.java index d4f630f..f5cc8d4 100644 --- a/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/api/AferoClientAPI.java +++ b/afero-sdk-client-retrofit2/src/main/java/io/afero/sdk/client/retrofit2/api/AferoClientAPI.java @@ -20,6 +20,7 @@ import io.afero.sdk.client.afero.models.InvitationDetails; import io.afero.sdk.client.afero.models.Location; import io.afero.sdk.client.afero.models.PostActionBody; +import io.afero.sdk.client.afero.models.ResetPasswordBody; import io.afero.sdk.client.afero.models.RuleExecuteBody; import io.afero.sdk.client.afero.models.ViewRequest; import io.afero.sdk.client.afero.models.ViewResponse; @@ -88,7 +89,7 @@ Observable resetPassword( @POST(V1 + "shortvalues/{resetCode}/passwordReset") Observable resetPasswordWithCode( @Path("resetCode") String resetCode, - @Body String newPassword + @Body ResetPasswordBody body ); @POST(V1 + "credentials/{email}/passwordReset") diff --git a/afero-sdk-core/src/main/java/io/afero/sdk/client/afero/models/ResetPasswordBody.java b/afero-sdk-core/src/main/java/io/afero/sdk/client/afero/models/ResetPasswordBody.java new file mode 100644 index 0000000..067c9ed --- /dev/null +++ b/afero-sdk-core/src/main/java/io/afero/sdk/client/afero/models/ResetPasswordBody.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014-2019 Afero, Inc. All rights reserved. + */ + +package io.afero.sdk.client.afero.models; + + +public class ResetPasswordBody { + public String password; + + public ResetPasswordBody(String pw) { + password = pw; + } +} diff --git a/afero-sdk-softhub/build.gradle b/afero-sdk-softhub/build.gradle index d0d76f3..15d01ce 100644 --- a/afero-sdk-softhub/build.gradle +++ b/afero-sdk-softhub/build.gradle @@ -7,10 +7,6 @@ apply plugin: 'com.android.library' repositories { maven { url "https://afero.jfrog.io/afero/hubby-android" - credentials { - username = project.aferoArtifactoryUserName - password = project.aferoArtifactoryPassword - } } } diff --git a/samples/afero-lab/app/build.gradle b/samples/afero-lab/app/build.gradle index b0e3cc0..7538bfb 100644 --- a/samples/afero-lab/app/build.gradle +++ b/samples/afero-lab/app/build.gradle @@ -5,12 +5,16 @@ apply plugin: 'com.android.application' final String sdkRepoKey = project.findProperty('aferoSDKConsumeRepoKey') ?: 'afero-java-sdk' -final String sdkVersion = project.findProperty('aferoSDKVersion') ?: '1.4.4' +final String sdkVersion = project.findProperty('aferoSDKVersion') ?: '1.4.5' repositories { maven { url "https://afero.jfrog.io/afero/${sdkRepoKey}" artifactUrls "https://afero.jfrog.io/afero/hubby-android" + credentials { + username = project.aferoArtifactoryUserName + password = project.aferoArtifactoryPassword + } } } configurations.all { @@ -95,4 +99,5 @@ dependencies { testImplementation 'junit:junit:4.12' implementation 'com.android.support:cardview-v7:27.1.1' + implementation 'com.android.support:design:27.1.1' } diff --git a/samples/afero-lab/app/src/main/java/io/afero/aferolab/MainActivity.java b/samples/afero-lab/app/src/main/java/io/afero/aferolab/MainActivity.java index 441b35a..b843d6e 100644 --- a/samples/afero-lab/app/src/main/java/io/afero/aferolab/MainActivity.java +++ b/samples/afero-lab/app/src/main/java/io/afero/aferolab/MainActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017 Afero, Inc. All rights reserved. + * Copyright (c) 2014-2019 Afero, Inc. All rights reserved. */ package io.afero.aferolab; @@ -26,6 +26,7 @@ import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; import butterknife.OnEditorAction; import io.afero.aferolab.addDevice.AddDeviceView; import io.afero.aferolab.addDevice.AddSetupModeDeviceView; @@ -35,6 +36,7 @@ import io.afero.aferolab.helper.BackStack; import io.afero.aferolab.helper.PermissionsHelper; import io.afero.aferolab.helper.PrefsHelper; +import io.afero.aferolab.resetPassword.RequestCodeView; import io.afero.aferolab.widget.AferoEditText; import io.afero.aferolab.widget.ScreenView; import io.afero.sdk.android.clock.AndroidClock; @@ -369,14 +371,25 @@ public void onNext(AddSetupModeDeviceView addDeviceView) {} public boolean onEditorActionSignIn(TextView textView, int actionId, KeyEvent event) { if (AferoEditText.isDone(actionId, event)) { if (textView.getId() == R.id.edit_text_password) { - startSignIn(mEmailEditText.getText().toString(), mPasswordEditText.getText().toString()); - mPasswordEditText.hideKeyboard(); + onClickSignIn(); } } return true; } + @OnClick(R.id.button_sign_in) + public void onClickSignIn() { + mPasswordEditText.hideKeyboard(); + startSignIn(mEmailEditText.getText().toString(), mPasswordEditText.getText().toString()); + } + + @OnClick(R.id.button_forgot_password) + public void onClickForgotPassword() { + mPasswordEditText.hideKeyboard(); + RequestCodeView.create(mRootView).start(mAferoClient); + } + private void startSignIn(String email, String password) { mSignInGroup.setVisibility(View.GONE); mStatusGroup.setVisibility(View.VISIBLE); diff --git a/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeController.java b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeController.java new file mode 100644 index 0000000..d75287e --- /dev/null +++ b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeController.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014-2019 Afero, Inc. All rights reserved. + */ + +package io.afero.aferolab.resetPassword; + +import io.afero.aferolab.BuildConfig; +import io.afero.sdk.client.afero.AferoClient; +import rx.Observer; +import rx.android.schedulers.AndroidSchedulers; + +class RequestCodeController { + + private RequestCodeView view; + private AferoClient aferoClient; + + RequestCodeController(RequestCodeView requestCodeView, AferoClient aferoClient) { + this.view = requestCodeView; + this.aferoClient = aferoClient; + } + + public void start() { + + } + + public void stop() { + + } + + void onClickRequestCode(String email) { + view.showProgress(); + aferoClient.sendPasswordRecoveryEmail(email, BuildConfig.APPLICATION_ID, "ANDROID") + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onCompleted() { + view.hideProgress(); + gotoPasswordResetView(); + } + + @Override + public void onError(Throwable e) { + view.hideProgress(); + view.showError(e, aferoClient.getStatusCode(e)); + } + + @Override + public void onNext(Void v) { + + } + }); + } + + void onClickAlreadyHaveCode() { + gotoPasswordResetView(); + } + + private void gotoPasswordResetView() { + ResetPasswordView.create(view.getRootView()).start(aferoClient); + view.stop(); + } +} diff --git a/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeView.java b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeView.java new file mode 100644 index 0000000..7277412 --- /dev/null +++ b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/RequestCodeView.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014-2019 Afero, Inc. All rights reserved. + */ + +package io.afero.aferolab.resetPassword; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.afero.aferolab.R; +import io.afero.aferolab.widget.AferoEditText; +import io.afero.aferolab.widget.ProgressSpinnerView; +import io.afero.aferolab.widget.ScreenView; +import io.afero.sdk.client.afero.AferoClient; + +public class RequestCodeView extends ScreenView { + + @BindView(R.id.edit_text_email) + AferoEditText emailEditText; + + @BindView(R.id.progress_request_code) + ProgressSpinnerView progressView; + + private RequestCodeController mController; + + + public RequestCodeView(@NonNull Context context) { + super(context); + } + + public RequestCodeView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RequestCodeView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public static RequestCodeView create(@NonNull View contextView) { + return inflateView(R.layout.view_request_code, contextView); + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + ButterKnife.bind(this); + } + + public RequestCodeView start(AferoClient aferoClient) { + pushOnBackStack(); + + mController = new RequestCodeController(this, aferoClient); + mController.start(); + + emailEditText.showKeyboard(); + + return this; + } + + @Override + public void stop() { + mController.stop(); + + super.stop(); + } + + @Override + public boolean onBackPressed() { + return false; + } + + @OnClick(R.id.button_request_code) + void onClickRequestCode() { + emailEditText.hideKeyboard(); + mController.onClickRequestCode(emailEditText.getText().toString()); + } + + @OnClick(R.id.button_already_have_code) + void onClickAlreadyHaveCode() { + emailEditText.hideKeyboard(); + mController.onClickAlreadyHaveCode(); + } + + void showProgress() { + progressView.show(); + } + + void hideProgress() { + progressView.hide(); + } +} diff --git a/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordController.java b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordController.java new file mode 100644 index 0000000..3222819 --- /dev/null +++ b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordController.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014-2019 Afero, Inc. All rights reserved. + */ + +package io.afero.aferolab.resetPassword; + +import io.afero.sdk.client.afero.AferoClient; +import rx.Observer; +import rx.android.schedulers.AndroidSchedulers; + +class ResetPasswordController { + + private ResetPasswordView view; + private AferoClient aferoClient; + + ResetPasswordController(ResetPasswordView resetPasswordView, AferoClient aferoClient) { + this.view = resetPasswordView; + this.aferoClient = aferoClient; + } + + public void start() { + + } + + public void stop() { + + } + + void onClickResetPassword(String code, String password) { + view.showProgress(); + aferoClient.resetPasswordWithCode(code, password) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onCompleted() { + view.stop(); + } + + @Override + public void onError(Throwable e) { + view.hideProgress(); + view.showError(e, aferoClient.getStatusCode(e)); + } + + @Override + public void onNext(Void aVoid) { + + } + }); + } +} diff --git a/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordView.java b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordView.java new file mode 100644 index 0000000..fd965ef --- /dev/null +++ b/samples/afero-lab/app/src/main/java/io/afero/aferolab/resetPassword/ResetPasswordView.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014-2019 Afero, Inc. All rights reserved. + */ + +package io.afero.aferolab.resetPassword; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.afero.aferolab.R; +import io.afero.aferolab.widget.AferoEditText; +import io.afero.aferolab.widget.ProgressSpinnerView; +import io.afero.aferolab.widget.ScreenView; +import io.afero.sdk.client.afero.AferoClient; + +public class ResetPasswordView extends ScreenView { + + @BindView(R.id.edit_text_reset_code) + AferoEditText resetCodeEditText; + + @BindView(R.id.edit_text_password) + AferoEditText passwordEditText; + + @BindView(R.id.progress_reset_password) + ProgressSpinnerView progressView; + + private ResetPasswordController mController; + + + public ResetPasswordView(@NonNull Context context) { + super(context); + } + + public ResetPasswordView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ResetPasswordView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public static ResetPasswordView create(@NonNull View contextView) { + return inflateView(R.layout.view_reset_password, contextView); + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + ButterKnife.bind(this); + } + + public ResetPasswordView start(AferoClient aferoClient) { + pushOnBackStack(); + + mController = new ResetPasswordController(this, aferoClient); + mController.start(); + + resetCodeEditText.showKeyboard(); + + return this; + } + + + @Override + public void stop() { + mController.stop(); + + super.stop(); + } + + @Override + public boolean onBackPressed() { + return false; + } + + @OnClick(R.id.button_reset_password) + void onClickRequestCode() { + resetCodeEditText.hideKeyboard(); + passwordEditText.hideKeyboard(); + mController.onClickResetPassword(resetCodeEditText.getText().toString(), passwordEditText.getText().toString()); + } + + void showProgress() { + progressView.show(); + } + + void hideProgress() { + progressView.hide(); + } +} diff --git a/samples/afero-lab/app/src/main/java/io/afero/aferolab/widget/ScreenView.java b/samples/afero-lab/app/src/main/java/io/afero/aferolab/widget/ScreenView.java index 85b5648..8cc90e6 100644 --- a/samples/afero-lab/app/src/main/java/io/afero/aferolab/widget/ScreenView.java +++ b/samples/afero-lab/app/src/main/java/io/afero/aferolab/widget/ScreenView.java @@ -8,6 +8,7 @@ import android.support.annotation.AttrRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -78,4 +79,12 @@ public static T inflateView(int layoutResId, View context //noinspection unchecked return (T) v; } + + public void showError(Throwable e, int statusCode) { + String errorString = getContext().getString(R.string.generic_error); + if (statusCode != 0) { + errorString += " (" + statusCode + ")"; + } + Snackbar.make(this, errorString, Snackbar.LENGTH_LONG).show(); + } } diff --git a/samples/afero-lab/app/src/main/res/layout/activity_main.xml b/samples/afero-lab/app/src/main/res/layout/activity_main.xml index 12c1e05..efa723d 100644 --- a/samples/afero-lab/app/src/main/res/layout/activity_main.xml +++ b/samples/afero-lab/app/src/main/res/layout/activity_main.xml @@ -4,8 +4,7 @@ ~ Copyright (c) 2014-2017 Afero, Inc. All rights reserved. --> - + app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> + android:minWidth="150dp" /> + android:minWidth="150dp" /> + + + +