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 Sep 10, 2024
1 parent 9b473c4 commit 9151f4f
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 12 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 @@ -59,6 +60,21 @@ private static void handleVoiceRecognition(
.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 @@ -46,4 +46,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,
content::WebContents* context_web_contents);
// Closes Leo chat window
void CloseActivity(content::WebContents* web_contents);
// Shows soft keyboard
void HandleShowSoftKeyboard(content::WebContents* web_contents);

} // namespace ai_chat

Expand Down
34 changes: 23 additions & 11 deletions browser/ui/webui/ai_chat/ai_chat_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "brave/browser/ui/side_panel/ai_chat/ai_chat_side_panel_utils.h"
#include "brave/browser/ui/webui/ai_chat/ai_chat_ui_page_handler.h"
#include "brave/browser/ui/webui/brave_webui_source.h"
#include "brave/components/ai_chat/content/browser/ai_chat_tab_helper.h"
#include "brave/components/ai_chat/core/browser/constants.h"
#include "brave/components/ai_chat/core/browser/utils.h"
#include "brave/components/ai_chat/core/common/pref_names.h"
Expand Down Expand Up @@ -93,6 +94,11 @@ AIChatUI::AIChatUI(content::WebUI* web_ui)
#endif

untrusted_source->AddBoolean("isMobile", kIsMobile);
ai_chat::AIChatTabHelper* active_chat_tab_helper =
ai_chat::AIChatTabHelper::FromWebContents(GetChatWebContents());
untrusted_source->AddBoolean(
"hasInitialHistory",
active_chat_tab_helper->GetVisibleConversationHistory().size() != 0);

untrusted_source->AddBoolean(
"hasUserDismissedPremiumPrompt",
Expand All @@ -118,21 +124,13 @@ AIChatUI::AIChatUI(content::WebUI* web_ui)

AIChatUI::~AIChatUI() = default;

void AIChatUI::BindInterface(
mojo::PendingReceiver<ai_chat::mojom::PageHandler> receiver) {
// We call ShowUI() before creating the PageHandler object so that
// the WebContents is added to a Browser which we can get a reference
// to and provide to the PageHandler.
if (embedder_) {
embedder_->ShowUI();
}

content::WebContents* AIChatUI::GetChatWebContents() {
content::WebContents* web_contents = nullptr;
#if !BUILDFLAG(IS_ANDROID)
Browser* browser =
ai_chat::GetBrowserForWebContents(web_ui()->GetWebContents());
if (!browser) {
return;
return web_contents;
}

TabStripModel* tab_strip_model = browser->tab_strip_model();
Expand All @@ -144,8 +142,22 @@ void AIChatUI::BindInterface(
if (web_contents == web_ui()->GetWebContents()) {
web_contents = nullptr;
}

return web_contents;
}

void AIChatUI::BindInterface(
mojo::PendingReceiver<ai_chat::mojom::PageHandler> receiver) {
// We call ShowUI() before creating the PageHandler object so that
// the WebContents is added to a Browser which we can get a reference
// to and provide to the PageHandler.
if (embedder_) {
embedder_->ShowUI();
}

page_handler_ = std::make_unique<ai_chat::AIChatUIPageHandler>(
web_ui()->GetWebContents(), web_contents, profile_, std::move(receiver));
web_ui()->GetWebContents(), GetChatWebContents(), profile_,
std::move(receiver));
}

bool UntrustedChatUIConfig::IsWebUIEnabled(
Expand Down
2 changes: 2 additions & 0 deletions browser/ui/webui/ai_chat/ai_chat_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

namespace content {
class BrowserContext;
class WebContents;
}

class Profile;
Expand All @@ -42,6 +43,7 @@ class AIChatUI : public ui::UntrustedWebUIController {
static constexpr std::string GetWebUIName() { return "AIChatPanel"; }

private:
content::WebContents* GetChatWebContents();
std::unique_ptr<ai_chat::mojom::PageHandler> page_handler_;

base::WeakPtr<TopChromeWebUIController::Embedder> embedder_;
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 @@ -172,6 +172,12 @@ void AIChatUIPageHandler::HandleVoiceRecognition() {
#endif
}

void AIChatUIPageHandler::HandleShowSoftKeyboard() {
#if BUILDFLAG(IS_ANDROID)
ai_chat::HandleShowSoftKeyboard(web_contents());
#endif
}

void AIChatUIPageHandler::GetConversationHistory(
GetConversationHistoryCallback callback) {
if (!active_chat_tab_helper_) {
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 @@ -64,6 +64,7 @@ class AIChatUIPageHandler : public ai_chat::mojom::PageHandler,
mojom::ActionType action_type) override;
void SubmitSummarizationRequest() override;
void HandleVoiceRecognition() override;
void HandleShowSoftKeyboard() override;
void GetConversationHistory(GetConversationHistoryCallback callback) override;
void MarkAgreementAccepted() override;
void GetSuggestedQuestions(GetSuggestedQuestionsCallback 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 @@ -246,6 +246,7 @@ interface PageHandler {
SubmitHumanConversationEntry(string input);
SubmitHumanConversationEntryWithAction(string input, ActionType action_type);
HandleVoiceRecognition();
HandleShowSoftKeyboard();
SubmitSummarizationRequest();
MarkAgreementAccepted();
// Get associated page information. If there is none then |site_info| will be
Expand Down
15 changes: 14 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 @@ -27,6 +27,7 @@ type Props = Pick<AIChatContext,
| 'isToolsMenuOpen'
| 'setIsToolsMenuOpen'
| 'isMobile'
| 'hasInitialHistory'
| 'shouldDisableUserInput'
| 'hasAcceptedAgreement'>

Expand All @@ -41,6 +42,7 @@ function InputBox(props: InputBoxProps) {
}

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

Expand Down Expand Up @@ -76,10 +78,21 @@ function InputBox(props: InputBoxProps) {
}
}

const querySubmitted = React.useRef(false)

const maybeAutofocus = (node: HTMLTextAreaElement | null) => {
if (node && props.context.selectedActionType) {
if (!node) {
return
}

if (props.context.selectedActionType) {
node.focus()
}
if (props.context.isMobile && props.context.hasAcceptedAgreement &&
!props.context.hasInitialHistory && !querySubmitted.current) {
node.focus()
getPageHandlerInstance().pageHandler.handleShowSoftKeyboard()
}
}

return (
Expand Down
2 changes: 2 additions & 0 deletions components/ai_chat/resources/page/state/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface AIChatContext extends CharCountContext {
showAgreementModal: boolean
shouldSendPageContents: boolean
isMobile: boolean
hasInitialHistory: boolean
inputText: string
selectedActionType: mojom.ActionType | undefined
isToolsMenuOpen: boolean
Expand Down Expand Up @@ -85,6 +86,7 @@ export const defaultContext: AIChatContext = {
showAgreementModal: false,
shouldSendPageContents: true,
isMobile: false,
hasInitialHistory: false,
inputText: '',
selectedActionType: undefined,
isToolsMenuOpen: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ function DataContextProvider(props: DataContextProviderProps) {
}

const isMobile = React.useMemo(() => loadTimeData.getBoolean('isMobile'), [])
const hasInitialHistory = React.useMemo(() =>
loadTimeData.getBoolean('hasInitialHistory'), [])

React.useEffect(() => {
initialiseForTargetTab()
Expand Down Expand Up @@ -405,6 +407,7 @@ function DataContextProvider(props: DataContextProviderProps) {
showAgreementModal,
shouldSendPageContents: shouldSendPageContents && siteInfo?.isContentAssociationPossible,
isMobile,
hasInitialHistory,
inputText,
isCharLimitExceeded,
isCharLimitApproaching,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function BeginGeneration() {
isCharLimitExceeded: context.isCharLimitExceeded,
shouldDisableUserInput: context.isGenerating,
isMobile: false,
hasInitialHistory: false,
hasAcceptedAgreement: true
}} />
</ToolsButtonMenu>
Expand Down

0 comments on commit 9151f4f

Please sign in to comment.