Android: Implement favicons for Password Manager entries (#33868)

* Android: Implement favicons for Password Manager entries

We now display favicons for Password Manager as happens for our desktop
edition.

In order to do this, the C++ side now passes in the full origin so that
we have the clean, proper URI in order to fetch the favicon.

Resolves brave/brave-browser#52775
This commit is contained in:
Oliver
2026-04-08 18:56:21 +02:00
committed by GitHub
parent b62ded55b4
commit 3660b3622a
8 changed files with 112 additions and 10 deletions
@@ -0,0 +1,67 @@
// Copyright (c) 2026 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package org.chromium.chrome.browser.password_manager.settings;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import org.chromium.build.annotations.NullMarked;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
import org.chromium.url.GURL;
/**
* A Preference that lazily loads a favicon when it becomes visible on screen. The favicon is
* fetched only once when the view is first bound, and cached via {@link Preference#setIcon} for
* subsequent rebinds.
*/
@NullMarked
public class PasswordEntryPreference extends Preference {
private final GURL mOriginUrl;
private final FaviconHelper mFaviconHelper;
private final FaviconHelper.DefaultFaviconHelper mDefaultFaviconHelper;
private final Profile mProfile;
public PasswordEntryPreference(
Context context,
String originUrl,
FaviconHelper faviconHelper,
FaviconHelper.DefaultFaviconHelper defaultFaviconHelper,
Profile profile) {
super(context);
mOriginUrl = new GURL(originUrl);
mFaviconHelper = faviconHelper;
mDefaultFaviconHelper = defaultFaviconHelper;
mProfile = profile;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
if (getIcon() == null) {
int faviconSizePixels =
getContext()
.getResources()
.getDimensionPixelSize(R.dimen.omnibox_suggestion_favicon_size);
mFaviconHelper.getForeignFaviconImageForURL(
mProfile,
mOriginUrl,
faviconSizePixels,
(bitmap, iconUrl) -> {
if (bitmap == null) {
bitmap =
mDefaultFaviconHelper.getDefaultFaviconBitmap(
getContext(), iconUrl, true, false);
}
setIcon(new BitmapDrawable(getContext().getResources(), bitmap));
});
}
}
}
@@ -34,6 +34,7 @@ import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.supplier.MonotonicObservableSupplier;
import org.chromium.base.supplier.ObservableSuppliers;
import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
import org.chromium.build.annotations.EnsuresNonNull;
import org.chromium.build.annotations.Initializer;
import org.chromium.build.annotations.MonotonicNonNull;
import org.chromium.build.annotations.NullMarked;
@@ -47,6 +48,7 @@ import org.chromium.chrome.browser.settings.ChromeBaseSettingsFragment;
import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
import org.chromium.chrome.browser.settings.MainSettings;
import org.chromium.chrome.browser.settings.search.ChromeBaseSearchIndexProvider;
import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
import org.chromium.components.browser_ui.settings.SearchUtils;
import org.chromium.components.browser_ui.settings.SearchViewProvider;
@@ -116,6 +118,8 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
private @Nullable Preference mLinkPref;
private /*@Nullable*/ Menu mMenu;
private @Nullable Preference mExportPasswordsPreference;
private @MonotonicNonNull FaviconHelper mFaviconHelper;
private @MonotonicNonNull FaviconHelper.DefaultFaviconHelper mDefaultFaviconHelper;
private @ManagePasswordsReferrer int mManagePasswordsReferrer;
private final SettableMonotonicObservableSupplier<String> mPageTitle =
@@ -134,6 +138,7 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
}
@Override
@EnsuresNonNull({"mFaviconHelper", "mDefaultFaviconHelper"})
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
mExportFlow.onCreate(
savedInstanceState,
@@ -190,6 +195,9 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
});
mPageTitle.set(getString(R.string.password_manager_settings_title));
mFaviconHelper = new FaviconHelper();
mDefaultFaviconHelper = new FaviconHelper.DefaultFaviconHelper();
// Load preferences from XML instead of creating programmatically
SettingsUtils.addPreferencesFromResource(this, R.xml.brave_password_settings_preferences);
@@ -540,7 +548,13 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
if (shouldBeFiltered(url, name)) {
continue; // The current password won't show with the active filter, try the next.
}
Preference preference = new Preference(getStyledContext());
PasswordEntryPreference preference =
new PasswordEntryPreference(
getStyledContext(),
saved.getOriginUrl(),
assumeNonNull(mFaviconHelper),
assumeNonNull(mDefaultFaviconHelper),
getProfile());
preference.setTitle(url);
preference.setOnPreferenceClickListener(this);
preference.setSummary(name);
@@ -671,6 +685,7 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
// by the system.
if (getActivity().isFinishing()) {
PasswordManagerHandlerProvider.getForProfile(getProfile()).removeObserver(this);
assumeNonNull(mFaviconHelper).destroy();
}
}