blob: 5d4c4a0d4ffd766b543f2a1e5c207b9d5c5ce750 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/menus/android/menu_model_bridge.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/menu_model.h"
#include "ui/gfx/android/java_bitmap.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "ui/android/menu_model_bridge_jni_headers/MenuModelBridge_jni.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaGlobalRef;
namespace ui {
MenuModelBridge::MenuModelBridge(base::WeakPtr<ui::MenuModel> menu_model)
: menu_model_(std::move(menu_model)) {
JNIEnv* env = base::android::AttachCurrentThread();
java_obj_ =
Java_MenuModelBridge_create(env, reinterpret_cast<intptr_t>(this));
AddExtensionItems();
}
MenuModelBridge::~MenuModelBridge() {
Java_MenuModelBridge_destroyNative(base::android::AttachCurrentThread(),
java_obj_);
}
void MenuModelBridge::ActivatedAt(JNIEnv* env, size_t i) {
if (!menu_model_) {
return;
}
menu_model_->ActivatedAt(i);
}
ScopedJavaGlobalRef<jobject> MenuModelBridge::GetJavaObject() {
return java_obj_;
}
// private
void MenuModelBridge::AddExtensionItems() {
if (!menu_model_) {
return;
}
base::ElapsedTimer timer;
JNIEnv* env = base::android::AttachCurrentThread();
for (size_t i = 0; i < menu_model_->GetItemCount(); ++i) {
if (!menu_model_->IsVisibleAt(i)) {
continue;
}
switch (menu_model_->GetTypeAt(i)) {
case MenuModel::TYPE_COMMAND:
case MenuModel::TYPE_HIGHLIGHTED: {
/* Translate both of these to basic items for now. */
ImageModel image = menu_model_->GetIconAt(i);
std::optional<SkBitmap> optional_bitmap;
if (image.IsImage()) {
optional_bitmap = image.GetImage().AsBitmap();
}
Java_MenuModelBridge_addCommand(
env, java_obj_, menu_model_->GetLabelAt(i), optional_bitmap,
menu_model_->IsEnabledAt(i), i);
break;
}
case MenuModel::TYPE_CHECK:
Java_MenuModelBridge_addCheck(
env, java_obj_, menu_model_->GetLabelAt(i),
menu_model_->IsItemCheckedAt(i), menu_model_->IsEnabledAt(i), i);
break;
case MenuModel::TYPE_RADIO:
Java_MenuModelBridge_addRadioButton(
env, java_obj_, menu_model_->GetLabelAt(i),
menu_model_->IsItemCheckedAt(i), menu_model_->IsEnabledAt(i), i);
break;
case MenuModel::TYPE_SEPARATOR: {
Java_MenuModelBridge_addDivider(env, java_obj_);
break;
}
/* Don't handle TYPE_BUTTON_ITEM for now; it's not available in the Chrome
* extensions API. */
case MenuModel::TYPE_SUBMENU: {
ImageModel image = menu_model_->GetIconAt(i);
std::optional<SkBitmap> optional_bitmap;
if (image.IsImage()) {
optional_bitmap = image.GetImage().AsBitmap();
}
auto submenu_model_bridge = std::make_unique<MenuModelBridge>(
menu_model_->GetSubmenuModelAt(i)->AsWeakPtr());
Java_MenuModelBridge_addSubmenu(
env, java_obj_, menu_model_->GetLabelAt(i), optional_bitmap,
menu_model_->IsEnabledAt(i), submenu_model_bridge->GetJavaObject());
submenu_model_bridges_.push_back(std::move(submenu_model_bridge));
break;
}
/* Don't handle TYPE_ACTIONABLE_SUBMENU for now; it's not available in the
* Chrome extensions API. */
case MenuModel::TYPE_TITLE:
// TODO(jhimawan): Call Java MenuModelBridge to add title item.
break;
default:
break;
/* Do nothing. */
}
}
base::UmaHistogramCustomMicrosecondsTimes(
"MenuModelBridge.AddExtensionItems.Duration",
base::Microseconds(timer.Elapsed().InMicrosecondsF()),
base::Microseconds(1), base::Microseconds(2000), 100);
}
} // namespace ui