blob: 4ba3d5bf16f3c250d45fb1adf782ac08b5b37df9 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.ui.signin.account_picker;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import android.widget.ViewFlipper;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.chrome.browser.signin.services.DisplayableProfileData;
import org.chromium.chrome.browser.ui.signin.R;
import org.chromium.chrome.browser.ui.signin.SigninUtils;
import org.chromium.chrome.browser.ui.signin.account_picker.AccountPickerBottomSheetProperties.ViewState;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.ui.widget.ButtonCompat;
import org.chromium.ui.widget.TextViewWithLeading;
/**
* This class is the AccountPickerBottomsheet view for the web sign-in flow.
*
* The bottom sheet shows a single account with a |Continue as ...| button by default, clicking
* on the account will expand the bottom sheet to an account list together with other sign-in
* options like "Add account".
*/
class AccountPickerBottomSheetView implements BottomSheetContent {
/**
* Listener for the back-press button.
*/
interface BackPressListener {
/**
* Notifies when user clicks the back-press button.
* @return true if the listener handles the back press, false if not.
*/
boolean onBackPressed();
/**
* @return A supplier that determines if back press will be handled by the sheet content.
*/
default ObservableSupplierImpl<Boolean> getBackPressStateChangedSupplier() {
ObservableSupplierImpl<Boolean> supplier = new ObservableSupplierImpl<>();
supplier.set(false);
return supplier;
}
}
/**
* The title id for each screen of the bottom sheet's view flipper, the position of
* each id corresponds to the value of {@link ViewState}. It is used to set focus
* on title when the view flipper moves to a new screen.
*/
private static final @IdRes int[] sTitleIds = new int[] {
R.id.account_picker_header_title,
R.id.account_picker_header_title,
R.id.account_picker_header_title,
R.id.account_picker_signin_in_progress_title,
R.id.account_picker_general_error_title,
R.id.account_picker_auth_error_title,
};
private final Activity mActivity;
private final BackPressListener mBackPressListener;
private final View mContentView;
private final ViewFlipper mViewFlipper;
private final RecyclerView mAccountListView;
private final View mSelectedAccountView;
private final ButtonCompat mDismissButton;
/**
* @param activity The activity that hosts this view. Used for inflating views.
* @param backPressListener The listener to be notified when the user taps the back button.
*/
AccountPickerBottomSheetView(Activity activity, BackPressListener backPressListener) {
mActivity = activity;
mBackPressListener = backPressListener;
mContentView = LayoutInflater.from(mActivity).inflate(
R.layout.account_picker_bottom_sheet_view, null);
mViewFlipper = mContentView.findViewById(R.id.account_picker_state_view_flipper);
checkViewFlipperChildrenAndViewStateMatch(mViewFlipper);
mAccountListView = mViewFlipper.getChildAt(ViewState.EXPANDED_ACCOUNT_LIST)
.findViewById(R.id.account_picker_account_list);
mAccountListView.setLayoutManager(new LinearLayoutManager(
mAccountListView.getContext(), LinearLayoutManager.VERTICAL, false));
mSelectedAccountView = mViewFlipper.getChildAt(ViewState.COLLAPSED_ACCOUNT_LIST)
.findViewById(R.id.account_picker_selected_account);
mDismissButton = mViewFlipper.getChildAt(ViewState.COLLAPSED_ACCOUNT_LIST)
.findViewById(R.id.account_picker_dismiss_button);
setUpContinueButton(mViewFlipper.getChildAt(ViewState.NO_ACCOUNTS),
R.string.signin_add_account_to_device);
setUpContinueButton(mViewFlipper.getChildAt(ViewState.SIGNIN_GENERAL_ERROR),
R.string.signin_account_picker_general_error_button);
setUpContinueButton(mViewFlipper.getChildAt(ViewState.SIGNIN_AUTH_ERROR),
R.string.auth_error_card_button);
}
/**
* The account list view is visible when the account list is expanded.
*/
RecyclerView getAccountListView() {
return mAccountListView;
}
/**
* The selected account is visible when the account list is collapsed.
*/
View getSelectedAccountView() {
return mSelectedAccountView;
}
/**
* Sets the listener of the continue button.
*/
void setOnClickListenerOfContinueButton(OnClickListener listener) {
for (int i = 0; i < mViewFlipper.getChildCount(); ++i) {
ButtonCompat continueButton =
mViewFlipper.getChildAt(i).findViewById(R.id.account_picker_continue_as_button);
if (continueButton != null) {
continueButton.setOnClickListener(listener);
}
}
}
/**
* The button to dismiss the bottom sheet.
*/
ButtonCompat getDismissButton() {
return mDismissButton;
}
/**
* Sets the displayed view according to the given {@link ViewState}.
*/
void setDisplayedView(@ViewState int state) {
mViewFlipper.setDisplayedChild(state);
View titleView = mViewFlipper.getChildAt(state).findViewById(sTitleIds[state]);
titleView.setFocusable(true);
titleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
/**
* Updates the views related to the selected account.
*
* This method only updates the UI elements like text related to the selected account, it
* does not change the visibility.
*/
void updateSelectedAccount(DisplayableProfileData accountProfileData) {
View view = mViewFlipper.getChildAt(ViewState.COLLAPSED_ACCOUNT_LIST);
ExistingAccountRowViewBinder.bindAccountView(accountProfileData, mSelectedAccountView);
ButtonCompat continueButton = view.findViewById(R.id.account_picker_continue_as_button);
continueButton.setText(
SigninUtils.getContinueAsButtonText(view.getContext(), accountProfileData));
}
/**
* Adjusts the strings in the header and dismiss button for the send-tab-to-self entry point.
*/
void setSendTabToSelfHeaderAndDismissButtonText() {
setSendTabToSelfHeaderText(ViewState.COLLAPSED_ACCOUNT_LIST);
setSendTabToSelfHeaderText(ViewState.EXPANDED_ACCOUNT_LIST);
setSendTabToSelfHeaderText(ViewState.NO_ACCOUNTS);
mDismissButton.setText(R.string.cancel);
}
@Override
public View getContentView() {
return mContentView;
}
@Nullable
@Override
public View getToolbarView() {
return null;
}
@Override
public int getVerticalScrollOffset() {
return 0;
}
@Override
public int getPeekHeight() {
return HeightMode.DISABLED;
}
@Override
public float getFullHeightRatio() {
return HeightMode.WRAP_CONTENT;
}
@Override
public void destroy() {}
@Override
public int getPriority() {
return ContentPriority.HIGH;
}
@Override
public boolean swipeToDismissEnabled() {
return true;
}
@Override
public boolean handleBackPress() {
return mBackPressListener.onBackPressed();
}
@Override
public ObservableSupplierImpl<Boolean> getBackPressStateChangedSupplier() {
return mBackPressListener.getBackPressStateChangedSupplier();
}
@Override
public void onBackPressed() {
mBackPressListener.onBackPressed();
}
@Override
public int getSheetContentDescriptionStringId() {
return R.string.signin_account_picker_bottom_sheet_subtitle;
}
@Override
public int getSheetHalfHeightAccessibilityStringId() {
return R.string.account_picker_bottom_sheet_accessibility_opened;
}
@Override
public int getSheetFullHeightAccessibilityStringId() {
return R.string.account_picker_bottom_sheet_accessibility_opened;
}
@Override
public int getSheetClosedAccessibilityStringId() {
return R.string.account_picker_bottom_sheet_accessibility_closed;
}
private void setSendTabToSelfHeaderText(@ViewState int viewState) {
final View view = mViewFlipper.getChildAt(viewState);
((TextView) view.findViewById(R.id.account_picker_header_title))
.setText(R.string.signin_account_picker_bottom_sheet_title_for_send_tab_to_self);
((TextViewWithLeading) view.findViewById(R.id.account_picker_header_subtitle))
.setText(R.string.signin_account_picker_bottom_sheet_subtitle_for_send_tab_to_self);
}
private static void setUpContinueButton(View view, @StringRes int buttonId) {
ButtonCompat continueButton = view.findViewById(R.id.account_picker_continue_as_button);
continueButton.setText(buttonId);
}
private static void checkViewFlipperChildrenAndViewStateMatch(ViewFlipper viewFlipper) {
checkViewFlipperChildIdAndViewStateMatch(
viewFlipper, ViewState.NO_ACCOUNTS, R.id.account_picker_state_no_account);
checkViewFlipperChildIdAndViewStateMatch(
viewFlipper, ViewState.COLLAPSED_ACCOUNT_LIST, R.id.account_picker_state_collapsed);
checkViewFlipperChildIdAndViewStateMatch(
viewFlipper, ViewState.EXPANDED_ACCOUNT_LIST, R.id.account_picker_state_expanded);
checkViewFlipperChildIdAndViewStateMatch(viewFlipper, ViewState.SIGNIN_IN_PROGRESS,
R.id.account_picker_state_signin_in_progress);
checkViewFlipperChildIdAndViewStateMatch(viewFlipper, ViewState.SIGNIN_GENERAL_ERROR,
R.id.account_picker_state_general_error);
checkViewFlipperChildIdAndViewStateMatch(
viewFlipper, ViewState.SIGNIN_AUTH_ERROR, R.id.account_picker_state_auth_error);
}
private static void checkViewFlipperChildIdAndViewStateMatch(
ViewFlipper viewFlipper, @ViewState int viewState, @IdRes int expectedChildId) {
if (viewFlipper.getChildAt(viewState).getId() != expectedChildId) {
throw new IllegalArgumentException("Match failed with ViewState:" + viewState);
}
}
}