Skip to content

Commit

Permalink
Autofocuses input box on Android Leo chat window on a fresh chat
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeyZhukovsky committed Jan 17, 2025
1 parent 4f4a7e3 commit 0cdd853
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import android.app.Activity;
import android.content.Context;
import android.view.inputmethod.InputMethodManager;

import org.jni_zero.CalledByNative;

Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions browser/ui/webui/ai_chat/ai_chat_ui_page_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
1 change: 1 addition & 0 deletions browser/ui/webui/ai_chat/ai_chat_ui_page_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<mojom::ChatUI> chat_ui,
SetChatUICallback callback) override;
Expand Down
1 change: 1 addition & 0 deletions components/ai_chat/core/common/mojom/ai_chat.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
11 changes: 10 additions & 1 deletion components/ai_chat/resources/page/components/input_box/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ type Props = Pick<
interface InputBoxProps {
context: Props
onFocusInputMobile?: () => unknown
maybeShowSoftKeyboard?: (querySubmitted: boolean) => unknown
}

function InputBox(props: InputBoxProps) {
const onInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
props.context.setInputText(e.target.value)
}

const querySubmitted = React.useRef(false)

const handleSubmit = () => {
querySubmitted.current = true
props.context.submitInputTextToAPI()
}

Expand Down Expand Up @@ -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()
}
}
Expand Down
12 changes: 12 additions & 0 deletions components/ai_chat/resources/page/components/main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<main className={styles.main}>
{isConversationListOpen && !aiChatContext.isStandalone && (
Expand Down Expand Up @@ -350,6 +361,7 @@ function Main() {
<InputBox
context={{ ...conversationContext, ...aiChatContext }}
onFocusInputMobile={handleOnFocusInputMobile}
maybeShowSoftKeyboard={maybeShowSoftKeyboard}
/>
</ToolsButtonMenu>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface CharCountContext {
}

export type ConversationContext = SendFeedbackState & CharCountContext & {
historyInitialized: boolean
conversationUuid?: string
conversationHistory: Mojom.ConversationTurn[]
associatedContentInfo?: Mojom.SiteInfo
Expand Down Expand Up @@ -69,6 +70,7 @@ export const defaultCharCountContext: CharCountContext = {
}

const defaultContext: ConversationContext = {
historyInitialized: false,
conversationHistory: [],
allModels: [],
suggestedQuestions: [],
Expand Down Expand Up @@ -189,7 +191,8 @@ export function ConversationContextProvider(props: React.PropsWithChildren) {
const { conversationHistory } =
await conversationHandler.getConversationHistory()
setPartialContext({
conversationHistory
conversationHistory,
historyInitialized: true
})
}

Expand Down

0 comments on commit 0cdd853

Please sign in to comment.