Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..080ecde03b766807892b96e8bac376f09069df9d |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java |
| @@ -0,0 +1,184 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// 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.locale; |
| + |
| +import android.content.Context; |
| +import android.content.DialogInterface; |
| +import android.os.Bundle; |
| +import android.support.annotation.Nullable; |
| +import android.view.View; |
| +import android.widget.RadioButton; |
| +import android.widget.RadioGroup; |
| +import android.widget.RadioGroup.OnCheckedChangeListener; |
| + |
| +import org.chromium.base.library_loader.LibraryLoader; |
| +import org.chromium.chrome.R; |
| +import org.chromium.chrome.browser.infobar.InfoBarControlLayout; |
| +import org.chromium.chrome.browser.search_engines.TemplateUrlService; |
| +import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl; |
| +import org.chromium.chrome.browser.widget.PromoDialog; |
| + |
| +import java.util.ArrayList; |
| +import java.util.List; |
| +import java.util.Random; |
| + |
| +/** A dialog that forces the user to choose a default search engine. */ |
| +public class DefaultSearchEnginePromoDialog extends PromoDialog implements OnCheckedChangeListener { |
| + /** Used to determine the promo dialog contents. */ |
| + private final int mDialogType; |
| + |
| + /** Run when the dialog is dismissed after the user has chosen a search engine. */ |
| + private final Runnable mOnDismissed; |
| + |
| + private RadioGroup mRadioGroup; |
| + private String mSelectedKeyword; |
| + |
| + /** |
| + * Construct and show the dialog. Will be asynchronous if the TemplateUrlService has not yet |
| + * been loaded. |
| + */ |
| + public static void create( |
|
Ted C
2017/04/26 20:21:52
maybe this should be called show?
gone
2017/04/26 21:45:15
Done.
|
| + final Context context, final int dialogType, @Nullable final Runnable onDismissed) { |
|
Ted C
2017/04/26 20:21:52
Should/can dialogType have an associated IntDef?
gone
2017/04/26 21:45:15
Seems like it'd be fine to move it to always be ca
|
| + assert LibraryLoader.isInitialized(); |
| + |
| + // Load up the search engines. |
| + final TemplateUrlService instance = TemplateUrlService.getInstance(); |
| + instance.registerLoadListener(new TemplateUrlService.LoadListener() { |
| + @Override |
| + public void onTemplateUrlServiceLoaded() { |
| + instance.unregisterLoadListener(this); |
| + new DefaultSearchEnginePromoDialog(context, dialogType, onDismissed).show(); |
| + } |
| + }); |
| + instance.load(); |
| + } |
| + |
| + private DefaultSearchEnginePromoDialog( |
| + Context context, int dialogType, @Nullable Runnable onDismissed) { |
| + super(context); |
| + mDialogType = dialogType; |
| + mOnDismissed = onDismissed; |
| + setOnDismissListener(this); |
| + |
| + // No one should be able to bypass this dialog by clicking outside or by hitting back. |
| + setCancelable(false); |
| + setCanceledOnTouchOutside(false); |
| + } |
| + |
| + @Override |
| + protected DialogParams getDialogParams() { |
| + PromoDialog.DialogParams params = new PromoDialog.DialogParams(); |
| + params.headerStringResource = R.string.search_engine_dialog_title; |
| + params.footerStringResource = R.string.search_engine_dialog_footer; |
| + params.primaryButtonStringResource = R.string.ok; |
| + return params; |
| + } |
| + |
| + @Override |
| + protected void onCreate(Bundle savedInstanceState) { |
| + super.onCreate(savedInstanceState); |
| + assert LibraryLoader.isInitialized(); |
|
Ted C
2017/04/26 20:21:52
Since this happens in the one place you create it
gone
2017/04/26 21:45:15
Done.
|
| + |
| + // Set up the general UI. |
| + InfoBarControlLayout controls = addControlLayout(); |
| + updateButtonState(); |
| + |
| + // Load up the search engines. |
| + TemplateUrlService instance = TemplateUrlService.getInstance(); |
| + assert instance.isLoaded(); |
| + List<TemplateUrl> engines = null; |
| + if (mDialogType == LocaleManager.SEARCH_ENGINE_PROMO_SHOW_EXISTING) { |
| + engines = instance.getSearchEngines(); |
| + } else { |
| + // TODO(dfalcantara): Handle the new user case. |
| + assert false; |
| + } |
| + |
| + List<CharSequence> engineNames = new ArrayList<>(); |
| + List<String> engineKeywords = new ArrayList<>(); |
| + shuffleEngines(engines, engineNames, engineKeywords); |
| + |
| + // Add the search engines to the dialog. |
| + mRadioGroup = controls.addRadioButtons( |
| + engineNames, engineKeywords, InfoBarControlLayout.INVALID_INDEX); |
| + mRadioGroup.setOnCheckedChangeListener(this); |
| + } |
| + |
| + @Override |
| + public void onCheckedChanged(RadioGroup group, int checkedId) { |
| + mSelectedKeyword = null; |
| + |
| + // Run through the RadioGroup to figure out which one was checked. |
| + for (int i = 0; i < group.getChildCount(); i++) { |
| + View child = group.getChildAt(i); |
| + assert child instanceof RadioButton; |
| + |
| + RadioButton button = (RadioButton) child; |
| + if (button.isChecked()) { |
| + assert mSelectedKeyword == null; |
| + mSelectedKeyword = (String) button.getTag(); |
| + } |
| + } |
| + |
| + updateButtonState(); |
| + } |
| + |
| + @Override |
| + public void onClick(View view) { |
| + if (view.getId() == R.id.button_primary) { |
| + if (mSelectedKeyword == null) { |
| + updateButtonState(); |
| + return; |
| + } |
| + |
| + dismiss(); |
| + } else { |
| + assert false : "Unhandled click."; |
| + } |
| + |
| + // Don't propagate the click to the parent to prevent circumventing the dialog. |
| + } |
| + |
| + @Override |
| + public void onDismiss(DialogInterface dialog) { |
| + if (mSelectedKeyword == null) { |
| + // This shouldn't happen, but in case it does, finish the Activity so that the user has |
| + // to respond to the dialog next time. |
| + if (getOwnerActivity() != null) getOwnerActivity().finish(); |
|
Ted C
2017/04/26 20:21:52
should this be finish and remove task?
gone
2017/04/26 21:45:15
I'd argue for a regular finish() so that the activ
|
| + } else { |
| + TemplateUrlService.getInstance().setSearchEngine(mSelectedKeyword.toString()); |
| + if (mOnDismissed != null) mOnDismissed.run(); |
| + // TODO(dfalcantara): Prevent the dialog from appearing again. |
| + } |
| + } |
| + |
| + private void updateButtonState() { |
| + findViewById(R.id.button_primary).setEnabled(mSelectedKeyword != null); |
| + } |
| + |
| + private void shuffleEngines( |
| + List<TemplateUrl> engines, List<CharSequence> names, List<String> keywords) { |
| + ArrayList<Integer> indices = new ArrayList<>(); |
|
Ted C
2017/04/26 20:21:52
Could we use:
https://developer.android.com/refere
gone
2017/04/26 21:45:15
Works for me.
|
| + for (int i = 0; i < engines.size(); i++) indices.add(i); |
| + |
| + Random generator = new Random(); |
| + for (int i = 0; i < indices.size(); i++) { |
| + int remaining = indices.size() - i; |
| + int shuffleIndex = i + generator.nextInt(remaining); |
| + |
| + if (shuffleIndex != i) { |
| + int temp = indices.get(i); |
| + indices.set(i, indices.get(shuffleIndex)); |
| + indices.set(shuffleIndex, temp); |
| + } |
| + } |
| + |
| + for (Integer index : indices) { |
| + TemplateUrl engine = engines.get(index); |
| + names.add(engine.getShortName()); |
| + keywords.add(engine.getKeyword()); |
| + } |
| + } |
| +} |