1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.locale; 6 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 import android.widget.RadioGroup; 11 import android.widget.RadioGroup.OnCheckedChangeListener; 12 13 import androidx.annotation.Nullable; 14 15 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType; 16 import org.chromium.components.browser_ui.widget.RadioButtonLayout; 17 import org.chromium.components.search_engines.TemplateUrl; 18 19 import java.util.ArrayList; 20 import java.util.Collections; 21 import java.util.List; 22 23 /** Handles user interactions with a user dialog that lets them pick a default search engine. */ 24 public class DefaultSearchEngineDialogHelper implements OnCheckedChangeListener, OnClickListener { 25 /** Handles interactions with the TemplateUrlService and LocaleManager. */ 26 public static class HelperDelegate { 27 private final int mDialogType; 28 29 /** 30 * Basic constructor for the delegate. 31 * @param dialogType {@link SearchEnginePromoType} for the dialog this delegate belongs to. 32 */ HelperDelegate(@earchEnginePromoType int dialogType)33 public HelperDelegate(@SearchEnginePromoType int dialogType) { 34 mDialogType = dialogType; 35 } 36 37 /** Determine what search engines will be listed. */ getSearchEngines()38 protected List<TemplateUrl> getSearchEngines() { 39 List<TemplateUrl> templateUrls = 40 LocaleManager.getInstance().getSearchEnginesForPromoDialog(mDialogType); 41 return templateUrls; 42 } 43 44 /** Called when the search engine the user selected is confirmed to be the one they want. */ onUserSeachEngineChoice(List<String> keywords, String keyword)45 protected void onUserSeachEngineChoice(List<String> keywords, String keyword) { 46 LocaleManager.getInstance().onUserSearchEngineChoiceFromPromoDialog( 47 mDialogType, keywords, keyword); 48 } 49 } 50 51 private final HelperDelegate mDelegate; 52 private final Runnable mFinishRunnable; 53 private final Button mConfirmButton; 54 55 /** 56 * List of search engine keywords in the order shown to the user. 57 */ 58 private final List<String> mSearchEngineKeywords; 59 60 /** 61 * Keyword for the search engine that is selected in the RadioButtonLayout. 62 * This value is not locked into the TemplateUrlService until the user confirms it by clicking 63 * on {@link #mConfirmButton}. 64 */ 65 private String mCurrentlySelectedKeyword; 66 67 /** 68 * Keyword that is both selected and confirmed (with a click to {@link #mConfirmButton}). 69 */ 70 private String mConfirmedKeyword; 71 72 /** 73 * Constructs a DefaultSearchEngineDialogHelper. 74 * 75 * @param dialogType Dialog type to show. 76 * @param controls {@link RadioButtonLayout} that will contains all the engine options. 77 * @param confirmButton Button that the user clicks on to confirm their selection. 78 * @param finishRunnable Runs after the user has confirmed their selection. 79 */ DefaultSearchEngineDialogHelper(@earchEnginePromoType int dialogType, RadioButtonLayout controls, Button confirmButton, Runnable finishRunnable)80 public DefaultSearchEngineDialogHelper(@SearchEnginePromoType int dialogType, 81 RadioButtonLayout controls, Button confirmButton, Runnable finishRunnable) { 82 mConfirmButton = confirmButton; 83 mConfirmButton.setOnClickListener(this); 84 mFinishRunnable = finishRunnable; 85 mDelegate = createDelegate(dialogType); 86 87 // Shuffle up the engines. 88 List<TemplateUrl> engines = mDelegate.getSearchEngines(); 89 List<CharSequence> engineNames = new ArrayList<>(); 90 mSearchEngineKeywords = new ArrayList<>(); 91 Collections.shuffle(engines); 92 for (int i = 0; i < engines.size(); i++) { 93 TemplateUrl engine = engines.get(i); 94 engineNames.add(engine.getShortName()); 95 mSearchEngineKeywords.add(engine.getKeyword()); 96 } 97 98 // Add the search engines to the dialog without any of them being selected by default. 99 controls.addOptions(engineNames, mSearchEngineKeywords); 100 controls.selectChildAtIndex(RadioButtonLayout.INVALID_INDEX); 101 controls.setOnCheckedChangeListener(this); 102 103 // Disable the button until the user selects an option. 104 updateButtonState(); 105 } 106 107 /** @return Keyword that corresponds to the search engine that is currently selected. */ 108 @Nullable getCurrentlySelectedKeyword()109 public final String getCurrentlySelectedKeyword() { 110 // TODO(yusufo): All callers should check getConfirmedKeyword below. 111 return mCurrentlySelectedKeyword; 112 } 113 114 /** @return Keyword that corresponds to the search engine that is selected and confirmed. */ 115 @Nullable getConfirmedKeyword()116 public final String getConfirmedKeyword() { 117 return mConfirmedKeyword; 118 } 119 120 @Override onCheckedChanged(RadioGroup group, int checkedId)121 public final void onCheckedChanged(RadioGroup group, int checkedId) { 122 mCurrentlySelectedKeyword = (String) group.findViewById(checkedId).getTag(); 123 updateButtonState(); 124 } 125 126 @Override onClick(View view)127 public final void onClick(View view) { 128 if (view != mConfirmButton) { 129 // Don't propagate the click to the parent to prevent circumventing the dialog. 130 assert false : "Unhandled click."; 131 return; 132 } 133 134 if (mCurrentlySelectedKeyword == null) { 135 // The user clicked on the button, but they haven't clicked on an option, yet. 136 updateButtonState(); 137 return; 138 } 139 140 mConfirmedKeyword = mCurrentlySelectedKeyword; 141 142 mDelegate.onUserSeachEngineChoice(mSearchEngineKeywords, mConfirmedKeyword.toString()); 143 mFinishRunnable.run(); 144 } 145 146 /** Creates the delegate that interacts with the TemplateUrlService. */ createDelegate(@earchEnginePromoType int dialogType)147 protected HelperDelegate createDelegate(@SearchEnginePromoType int dialogType) { 148 return new HelperDelegate(dialogType); 149 } 150 151 /** Prevent the user from moving forward until they've clicked a search engine. */ updateButtonState()152 private final void updateButtonState() { 153 mConfirmButton.setEnabled(mCurrentlySelectedKeyword != null); 154 } 155 } 156