From 0cdd8530e2f34429f5ce183c068e38edb9001382 Mon Sep 17 00:00:00 2001 From: Serg Date: Fri, 17 Jan 2025 13:24:41 -0500 Subject: [PATCH] Autofocuses input box on Android Leo chat window on a fresh chat --- .../BraveLeoSettingsLauncherHelper.java | 16 ++++++++++++++++ .../brave_leo_settings_launcher_helper.cc | 5 +++++ .../ai_chat/brave_leo_settings_launcher_helper.h | 2 ++ .../ui/webui/ai_chat/ai_chat_ui_page_handler.cc | 6 ++++++ .../ui/webui/ai_chat/ai_chat_ui_page_handler.h | 1 + .../ai_chat/core/common/mojom/ai_chat.mojom | 1 + .../page/components/input_box/index.tsx | 11 ++++++++++- .../resources/page/components/main/index.tsx | 12 ++++++++++++ .../page/state/conversation_context.tsx | 5 ++++- 9 files changed, 57 insertions(+), 2 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java index 2686f452c122..9709d2532fa9 100644 --- a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java +++ b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java @@ -7,6 +7,7 @@ import android.app.Activity; import android.content.Context; +import android.view.inputmethod.InputMethodManager; import org.jni_zero.CalledByNative; @@ -58,6 +59,21 @@ private static void handleVoiceRecognition(WebContents webContents, String conve .startVoiceRecognition(); } + @CalledByNative + private static void handleShowSoftKeyboard(WebContents webContents) { + WindowAndroid windowAndroid = webContents.getTopLevelNativeWindow(); + if (windowAndroid == null) { + return; + } + Activity activity = windowAndroid.getActivity().get(); + if (activity == null) { + return; + } + InputMethodManager imm = + (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(activity.getWindow().getCurrentFocus(), 0); + } + @CalledByNative private static void closeActivity(WebContents webContents) { WindowAndroid windowAndroid = webContents.getTopLevelNativeWindow(); diff --git a/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.cc b/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.cc index d152af15d9ce..f880c6ac3b8f 100644 --- a/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.cc +++ b/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.cc @@ -47,4 +47,9 @@ void CloseActivity(content::WebContents* web_contents) { base::android::AttachCurrentThread(), web_contents->GetJavaWebContents()); } +void HandleShowSoftKeyboard(content::WebContents* web_contents) { + Java_BraveLeoSettingsLauncherHelper_handleShowSoftKeyboard( + base::android::AttachCurrentThread(), web_contents->GetJavaWebContents()); +} + } // namespace ai_chat diff --git a/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.h b/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.h index d54ef86cb542..7d3a795a80d3 100644 --- a/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.h +++ b/browser/ui/android/ai_chat/brave_leo_settings_launcher_helper.h @@ -27,6 +27,8 @@ void HandleVoiceRecognition(content::WebContents* web_contents, const std::string& conversation_uuid); // Closes Leo chat window void CloseActivity(content::WebContents* web_contents); +// Shows soft keyboard +void HandleShowSoftKeyboard(content::WebContents* web_contents); } // namespace ai_chat diff --git a/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.cc b/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.cc index ee70c6c8a56f..d4c08dadbd01 100644 --- a/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.cc +++ b/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.cc @@ -94,6 +94,12 @@ void AIChatUIPageHandler::HandleVoiceRecognition( #endif } +void AIChatUIPageHandler::HandleShowSoftKeyboard() { +#if BUILDFLAG(IS_ANDROID) + ai_chat::HandleShowSoftKeyboard(owner_web_contents_.get()); +#endif +} + void AIChatUIPageHandler::OpenAIChatSettings() { content::WebContents* contents_to_navigate = (active_chat_tab_helper_) ? active_chat_tab_helper_->web_contents() diff --git a/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.h b/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.h index a8fa5d418829..03c2f87db0f0 100644 --- a/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.h +++ b/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.h @@ -53,6 +53,7 @@ class AIChatUIPageHandler : public mojom::AIChatUIHandler, void RefreshPremiumSession() override; void ManagePremium() override; void HandleVoiceRecognition(const std::string& conversation_uuid) override; + void HandleShowSoftKeyboard() override; void CloseUI() override; void SetChatUI(mojo::PendingRemote chat_ui, SetChatUICallback callback) override; diff --git a/components/ai_chat/core/common/mojom/ai_chat.mojom b/components/ai_chat/core/common/mojom/ai_chat.mojom index e6cc9b09c387..9e9aa20fe4dd 100644 --- a/components/ai_chat/core/common/mojom/ai_chat.mojom +++ b/components/ai_chat/core/common/mojom/ai_chat.mojom @@ -369,6 +369,7 @@ interface AIChatUIHandler { RefreshPremiumSession(); ManagePremium(); HandleVoiceRecognition(string conversation_uuid); + HandleShowSoftKeyboard(); // This might be a no-op if the UI isn't closeable CloseUI(); diff --git a/components/ai_chat/resources/page/components/input_box/index.tsx b/components/ai_chat/resources/page/components/input_box/index.tsx index 9dfb4012b622..3eef0ff34fa0 100644 --- a/components/ai_chat/resources/page/components/input_box/index.tsx +++ b/components/ai_chat/resources/page/components/input_box/index.tsx @@ -38,6 +38,7 @@ type Props = Pick< interface InputBoxProps { context: Props onFocusInputMobile?: () => unknown + maybeShowSoftKeyboard?: (querySubmitted: boolean) => unknown } function InputBox(props: InputBoxProps) { @@ -45,7 +46,10 @@ function InputBox(props: InputBoxProps) { props.context.setInputText(e.target.value) } + const querySubmitted = React.useRef(false) + const handleSubmit = () => { + querySubmitted.current = true props.context.submitInputTextToAPI() } @@ -86,7 +90,12 @@ function InputBox(props: InputBoxProps) { } const maybeAutofocus = (node: HTMLTextAreaElement | null) => { - if (node && props.context.selectedActionType) { + if (!node) { + return + } + if (props.context.selectedActionType || + props.maybeShowSoftKeyboard && + props.maybeShowSoftKeyboard(querySubmitted.current)) { node.focus() } } diff --git a/components/ai_chat/resources/page/components/main/index.tsx b/components/ai_chat/resources/page/components/main/index.tsx index eb956f55ef24..b8a92ae8da83 100644 --- a/components/ai_chat/resources/page/components/main/index.tsx +++ b/components/ai_chat/resources/page/components/main/index.tsx @@ -205,6 +205,17 @@ function Main() { } } + const maybeShowSoftKeyboard = (querySubmitted: boolean) => { + if (aiChatContext.isMobile && aiChatContext.hasAcceptedAgreement && + conversationContext.historyInitialized && !querySubmitted && + !conversationContext.isGenerating && + conversationContext.conversationHistory.length === 0) { + aiChatContext.uiHandler?.handleShowSoftKeyboard() + return true + } + return false + } + return (
{isConversationListOpen && !aiChatContext.isStandalone && ( @@ -350,6 +361,7 @@ function Main() { diff --git a/components/ai_chat/resources/page/state/conversation_context.tsx b/components/ai_chat/resources/page/state/conversation_context.tsx index d6d81be07b41..544ea5e74a0c 100644 --- a/components/ai_chat/resources/page/state/conversation_context.tsx +++ b/components/ai_chat/resources/page/state/conversation_context.tsx @@ -23,6 +23,7 @@ export interface CharCountContext { } export type ConversationContext = SendFeedbackState & CharCountContext & { + historyInitialized: boolean conversationUuid?: string conversationHistory: Mojom.ConversationTurn[] associatedContentInfo?: Mojom.SiteInfo @@ -69,6 +70,7 @@ export const defaultCharCountContext: CharCountContext = { } const defaultContext: ConversationContext = { + historyInitialized: false, conversationHistory: [], allModels: [], suggestedQuestions: [], @@ -189,7 +191,8 @@ export function ConversationContextProvider(props: React.PropsWithChildren) { const { conversationHistory } = await conversationHandler.getConversationHistory() setPartialContext({ - conversationHistory + conversationHistory, + historyInitialized: true }) }