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