From e1a3daade27d011c54d02c7af2664bfceb615d66 Mon Sep 17 00:00:00 2001 From: Jay Harris Date: Thu, 19 Dec 2024 07:41:30 +1300 Subject: [PATCH] [AI Chat]: Android history setting (#26988) --- android/brave_java_resources.gni | 1 + .../org/chromium/base/BraveFeatureList.java | 1 + .../browser/brave_leo/BraveLeoPrefUtils.java | 18 ++++++ .../browser/settings/BraveLeoPreferences.java | 56 ++++++++++++++++++- .../layout/brave_leo_clear_history_dialog.xml | 11 ++++ .../java/res/xml/brave_leo_preferences.xml | 6 ++ .../api/settings_private/brave_prefs_util.cc | 2 +- .../android/strings/android_brave_strings.grd | 9 +++ .../flags/android/chrome_feature_list.cc | 7 ++- .../ai_chat/core/browser/ai_chat_service.cc | 11 ++-- .../core/browser/ai_chat_service_unittest.cc | 4 +- components/ai_chat/core/common/pref_names.cc | 2 +- components/ai_chat/core/common/pref_names.h | 3 +- 13 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 android/java/res/layout/brave_leo_clear_history_dialog.xml diff --git a/android/brave_java_resources.gni b/android/brave_java_resources.gni index e2cd2c4bd6b2..f013687baf59 100644 --- a/android/brave_java_resources.gni +++ b/android/brave_java_resources.gni @@ -796,6 +796,7 @@ brave_java_resources = [ "java/res/layout/brave_custom_tabs_toolbar.xml", "java/res/layout/brave_dialog_preference.xml", "java/res/layout/brave_exit_confirmation.xml", + "java/res/layout/brave_leo_clear_history_dialog.xml", "java/res/layout/brave_leo_reset_dialog.xml", "java/res/layout/brave_news_card_menu.xml", "java/res/layout/brave_news_load_new_content.xml", diff --git a/android/java/org/chromium/base/BraveFeatureList.java b/android/java/org/chromium/base/BraveFeatureList.java index 5667c4c1fecf..78bdad5f2d3a 100644 --- a/android/java/org/chromium/base/BraveFeatureList.java +++ b/android/java/org/chromium/base/BraveFeatureList.java @@ -30,6 +30,7 @@ public abstract class BraveFeatureList { public static final String BRAVE_FORGET_FIRST_PARTY_STORAGE = "BraveForgetFirstPartyStorage"; public static final String BRAVE_REQUEST_OTR_TAB = "BraveRequestOTRTab"; public static final String AI_CHAT = "AIChat"; + public static final String AI_CHAT_HISTORY = "AIChatHistory"; public static final String BRAVE_SHOW_STRICT_FINGERPRINTING_MODE = "BraveShowStrictFingerprintingMode"; public static final String BRAVE_DAY_ZERO_EXPERIMENT = "BraveDayZeroExperiment"; diff --git a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java index dc1f6bd95872..020003a7e326 100644 --- a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java +++ b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java @@ -73,6 +73,24 @@ public static void setChatPurchaseToken(String token) { } } + public static boolean getIsHistoryEnabled() { + Profile profileToUse = BraveLeoPrefUtils.getProfile(); + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.getIsHistoryEnabled profile is null"); + return false; + } + return UserPrefs.get(profileToUse).getBoolean(BravePref.BRAVE_CHAT_STORAGE_ENABLED); + } + + public static void setIsHistoryEnabled(boolean isEnabled) { + Profile profileToUse = BraveLeoPrefUtils.getProfile(); + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.getIsHistoryEnabled profile is null"); + return; + } + UserPrefs.get(profileToUse).setBoolean(BravePref.BRAVE_CHAT_STORAGE_ENABLED, isEnabled); + } + private static void createFetchOrder(Profile profileToUse) { BraveLeoMojomHelper.getInstance(profileToUse) .createOrderId( diff --git a/android/java/org/chromium/chrome/browser/settings/BraveLeoPreferences.java b/android/java/org/chromium/chrome/browser/settings/BraveLeoPreferences.java index 55fa0b069a97..b097c452f1bc 100644 --- a/android/java/org/chromium/chrome/browser/settings/BraveLeoPreferences.java +++ b/android/java/org/chromium/chrome/browser/settings/BraveLeoPreferences.java @@ -5,14 +5,20 @@ package org.chromium.chrome.browser.settings; +import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import org.chromium.ai_chat.mojom.ModelWithSubtitle; import org.chromium.ai_chat.mojom.PremiumStatus; +import org.chromium.base.BraveFeatureList; import org.chromium.base.BravePreferenceKeys; import org.chromium.base.Log; import org.chromium.base.supplier.ObservableSupplier; @@ -23,6 +29,7 @@ import org.chromium.chrome.browser.brave_leo.BraveLeoMojomHelper; import org.chromium.chrome.browser.brave_leo.BraveLeoPrefUtils; import org.chromium.chrome.browser.brave_leo.BraveLeoUtils; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.preferences.ChromeSharedPreferences; import org.chromium.chrome.browser.util.TabUtils; import org.chromium.components.browser_ui.settings.ChromeBasePreference; @@ -36,10 +43,12 @@ public class BraveLeoPreferences extends BravePreferenceFragment private static final String PREF_MANAGE_SUBSCRIPTION = "subscription_manage"; private static final String PREF_GO_PREMIUM = "go_premium"; private static final String PREF_AUTOCOMPLETE = "autocomplete_switch"; + private static final String PREF_HISTORY = "history_switch"; private static final String PREF_SUBSCRIPTION_CATEGORY = "subscription_category"; private static final String PREF_DEFAULT_MODEL = "default_model"; private final ObservableSupplierImpl mPageTitle = new ObservableSupplierImpl<>(); + private ChromeSwitchPreference mHistory; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -66,6 +75,15 @@ public void onCreate(Bundle savedInstanceState) { BravePreferenceKeys.BRAVE_LEO_AUTOCOMPLETE, true)); } } + + Preference history = findPreference(PREF_HISTORY); + if (history instanceof ChromeSwitchPreference) { + mHistory = (ChromeSwitchPreference) history; + mHistory.setOnPreferenceChangeListener(this); + mHistory.setChecked(BraveLeoPrefUtils.getIsHistoryEnabled()); + mHistory.setVisible(ChromeFeatureList.isEnabled(BraveFeatureList.AI_CHAT_HISTORY)); + } + BraveLeoUtils.verifySubscription( (subscriptionActive) -> { checkLinkPurchase(); @@ -145,12 +163,48 @@ private void checkLinkPurchase() { }); } + private void showConfirmClearHistoryDialog() { + LayoutInflater inflater = + (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.brave_leo_clear_history_dialog, null); + + DialogInterface.OnClickListener onClickListener = + (dialog, button) -> { + if (button == AlertDialog.BUTTON_POSITIVE) { + BraveLeoPrefUtils.setIsHistoryEnabled(false); + mHistory.setChecked(false); + } else { + dialog.dismiss(); + } + }; + + AlertDialog.Builder alert = + new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog); + AlertDialog alertDialog = + alert.setTitle(R.string.leo_clear_history_title) + .setView(view) + .setPositiveButton(R.string.brave_leo_confirm_text, onClickListener) + .setNegativeButton(R.string.cancel, onClickListener) + .create(); + alertDialog.getDelegate().setHandleNativeActionModesEnabled(false); + alertDialog.show(); + } + @Override public boolean onPreferenceChange(@NonNull Preference preference, Object o) { String key = preference.getKey(); + boolean enabled = (boolean) o; if (PREF_AUTOCOMPLETE.equals(key)) { ChromeSharedPreferences.getInstance() - .writeBoolean(BravePreferenceKeys.BRAVE_LEO_AUTOCOMPLETE, (boolean) o); + .writeBoolean(BravePreferenceKeys.BRAVE_LEO_AUTOCOMPLETE, enabled); + } + if (PREF_HISTORY.equals(key)) { + if (enabled) { + BraveLeoPrefUtils.setIsHistoryEnabled(enabled); + } else { + showConfirmClearHistoryDialog(); + return false; + } } return true; diff --git a/android/java/res/layout/brave_leo_clear_history_dialog.xml b/android/java/res/layout/brave_leo_clear_history_dialog.xml new file mode 100644 index 000000000000..4e11763f4bb6 --- /dev/null +++ b/android/java/res/layout/brave_leo_clear_history_dialog.xml @@ -0,0 +1,11 @@ + + + + diff --git a/android/java/res/xml/brave_leo_preferences.xml b/android/java/res/xml/brave_leo_preferences.xml index 183087b667c5..4f0ffe6916ac 100644 --- a/android/java/res/xml/brave_leo_preferences.xml +++ b/android/java/res/xml/brave_leo_preferences.xml @@ -15,6 +15,12 @@ android:title="@string/leo_autocomplete_pref" android:defaultValue="true" /> + + Leo is an AI-powered smart assistant, built right into the browser + + Store conversation history + Show autocomplete suggestions in address bar @@ -3700,6 +3703,12 @@ If you don't accept this request, VPN will not reconnect and your internet conne Resetting the Leo assistant will require you to opt-in to use Leo in the future and will also clear your chat history. Clearing your chat history will delete all your previous conversations with Leo. This action cannot be undone. + + Clear chat history + + + Disabling chat history will delete all your previous conversations with Leo. This action cannot be undone. + Confirm diff --git a/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc b/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc index 8cc98935e6b1..3f2bf292f67f 100644 --- a/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc @@ -23,11 +23,12 @@ #include "net/base/features.h" #include "third_party/blink/public/common/features.h" -#define BRAVE_AI_CHAT_FLAG &ai_chat::features::kAIChat, +#define BRAVE_AI_CHAT_FLAGS \ + &ai_chat::features::kAIChat, &ai_chat::features::kAIChatHistory, // clang-format off #define kForceWebContentsDarkMode kForceWebContentsDarkMode, \ - BRAVE_AI_CHAT_FLAG \ + BRAVE_AI_CHAT_FLAGS \ &brave_rewards::features::kBraveRewards, \ &brave_search_conversion::features::kOmniboxBanner, \ &brave_vpn::features::kBraveVPNLinkSubscriptionAndroidUI, \ @@ -54,7 +55,7 @@ #include "src/chrome/browser/flags/android/chrome_feature_list.cc" #undef kForceWebContentsDarkMode -#undef BRAVE_AI_CHAT_FLAG +#undef BRAVE_AI_CHAT_FLAGS namespace chrome { namespace android { diff --git a/components/ai_chat/core/browser/ai_chat_service.cc b/components/ai_chat/core/browser/ai_chat_service.cc index 8dcbdbd0cda7..b4d449e50d90 100644 --- a/components/ai_chat/core/browser/ai_chat_service.cc +++ b/components/ai_chat/core/browser/ai_chat_service.cc @@ -115,7 +115,7 @@ AIChatService::AIChatService( base::BindRepeating(&AIChatService::OnUserOptedIn, weak_ptr_factory_.GetWeakPtr())); pref_change_registrar_.Add( - prefs::kStorageEnabled, + prefs::kBraveChatStorageEnabled, base::BindRepeating(&AIChatService::MaybeInitStorage, weak_ptr_factory_.GetWeakPtr())); pref_change_registrar_.Add( @@ -382,7 +382,7 @@ void AIChatService::OnOsCryptAsyncReady(os_crypt_async::Encryptor encryptor, return; } // Pref might have changed since we started this process - if (!profile_prefs_->GetBoolean(prefs::kStorageEnabled)) { + if (!profile_prefs_->GetBoolean(prefs::kBraveChatStorageEnabled)) { return; } ai_chat_db_ = base::SequenceBound( @@ -559,7 +559,7 @@ void AIChatService::MarkAgreementAccepted() { } void AIChatService::EnableStoragePref() { - profile_prefs_->SetBoolean(prefs::kStorageEnabled, true); + profile_prefs_->SetBoolean(prefs::kBraveChatStorageEnabled, true); } void AIChatService::DismissStorageNotice() { @@ -686,7 +686,8 @@ mojom::ServiceStatePtr AIChatService::BuildState() { !last_accepted_disclaimer.is_null() && last_accepted_disclaimer < base::Time::Now() - base::Days(1); - bool is_storage_enabled = profile_prefs_->GetBoolean(prefs::kStorageEnabled); + bool is_storage_enabled = + profile_prefs_->GetBoolean(prefs::kBraveChatStorageEnabled); mojom::ServiceStatePtr state = mojom::ServiceState::New(); state->has_accepted_agreement = is_user_opted_in; @@ -705,7 +706,7 @@ void AIChatService::OnStateChanged() { bool AIChatService::IsAIChatHistoryEnabled() { return (features::IsAIChatHistoryEnabled() && - profile_prefs_->GetBoolean(prefs::kStorageEnabled)); + profile_prefs_->GetBoolean(prefs::kBraveChatStorageEnabled)); } void AIChatService::OnRequestInProgressChanged(ConversationHandler* handler, diff --git a/components/ai_chat/core/browser/ai_chat_service_unittest.cc b/components/ai_chat/core/browser/ai_chat_service_unittest.cc index 12f4c2af8a57..60e464b31d4d 100644 --- a/components/ai_chat/core/browser/ai_chat_service_unittest.cc +++ b/components/ai_chat/core/browser/ai_chat_service_unittest.cc @@ -631,7 +631,7 @@ TEST_P(AIChatServiceUnitTest, MaybeInitStorage_DisableStoragePref) { ExpectVisibleConversationsSize(FROM_HERE, 3); // Disable storage - prefs_.SetBoolean(prefs::kStorageEnabled, false); + prefs_.SetBoolean(prefs::kBraveChatStorageEnabled, false); // Wait for OnConversationListChanged which indicates data has been removed task_environment_.RunUntilIdle(); @@ -648,7 +648,7 @@ TEST_P(AIChatServiceUnitTest, MaybeInitStorage_DisableStoragePref) { ExpectVisibleConversationsSize(FROM_HERE, 0); // Re-enable storage preference - prefs_.SetBoolean(prefs::kStorageEnabled, true); + prefs_.SetBoolean(prefs::kBraveChatStorageEnabled, true); // Conversations are no longer in persistant storage ExpectVisibleConversationsSize(FROM_HERE, 0); } diff --git a/components/ai_chat/core/common/pref_names.cc b/components/ai_chat/core/common/pref_names.cc index 0bb49c3eb447..7d7f4a573027 100644 --- a/components/ai_chat/core/common/pref_names.cc +++ b/components/ai_chat/core/common/pref_names.cc @@ -16,7 +16,7 @@ namespace ai_chat::prefs { void RegisterProfilePrefs(PrefRegistrySimple* registry) { if (ai_chat::features::IsAIChatEnabled()) { registry->RegisterTimePref(kLastAcceptedDisclaimer, {}); - registry->RegisterBooleanPref(kStorageEnabled, true); + registry->RegisterBooleanPref(kBraveChatStorageEnabled, true); registry->RegisterBooleanPref(kBraveChatAutocompleteProviderEnabled, true); registry->RegisterBooleanPref(kUserDismissedPremiumPrompt, false); registry->RegisterBooleanPref(kUserDismissedStorageNotice, false); diff --git a/components/ai_chat/core/common/pref_names.h b/components/ai_chat/core/common/pref_names.h index 1222461b4bd4..3cdca39ce31d 100644 --- a/components/ai_chat/core/common/pref_names.h +++ b/components/ai_chat/core/common/pref_names.h @@ -15,7 +15,8 @@ namespace ai_chat::prefs { inline constexpr char kLastAcceptedDisclaimer[] = "brave.ai_chat.last_accepted_disclaimer"; -inline constexpr char kStorageEnabled[] = "brave.ai_chat.storage_enabled"; +inline constexpr char kBraveChatStorageEnabled[] = + "brave.ai_chat.storage_enabled"; inline constexpr char kBraveChatAutocompleteProviderEnabled[] = "brave.ai_chat.autocomplete_provider_enabled"; inline constexpr char kBraveChatP3AChatCountWeeklyStorage[] =