11#include <QtCore/qdatetime.h>
12#include <QtCore/qobject.h>
13#include <QtGui/private/qhighdpiscaling_p.h>
14#include <QtGui/qguiapplication.h>
15#include <QtGui/qicon.h>
16#include <QtGui/qpa/qplatformmenu.h>
17#include <QtGui/qscreen.h>
20#include <qohosplugincore.h>
37 return QNapi::Array::New(jsState.env());
45 qOhosReportFatalErrorAndAbort(
"%s: Default QAbilityPeer is not a QUiAbilityPeer", Q_FUNC_INFO);
47 return qUiAbilityPeer->qAbility().eval<QNapi::Object>(
"context");
53 return QtOhos::registerOnOffMethodsBasedEventHandler(
54 jsState.eval<QNapi::Object>(
"@kit.StatusBarExtensionKit.statusBarManager"),
56 [leftClickListener = std::move(leftClickListener)](
const QtOhos::
CallbackInfo &cbInfo) {
57 auto eventData = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
58 auto optIconClickType = QNapi::getOptionalPropOrEmpty<QNapi::String>(
59 QNapi::getOptionalPropOrEmpty<QNapi::Object>(eventData,
"data"),
62 if (optIconClickType.IsEmpty()) {
64 "%s: no 'iconClickType' in the event (%s), ignoring it",
65 Q_FUNC_INFO, QNapi::toJsonString(eventData).c_str());
69 if (optIconClickType.Utf8Value() ==
"leftClick")
77 "@kit.StatusBarExtensionKit.statusBarManager.addToStatusBar(*)",
78 {getContextForStatusBarManager(jsState), statusBarItem});
80 qOhosPrintfDebug(
"%s: Successfully added icon to status bar [Statusbar] ", Q_FUNC_INFO);
86 "@kit.StatusBarExtensionKit.statusBarManager.removeFromStatusBar(*)",
87 {getContextForStatusBarManager(jsState)});
89 qOhosPrintfDebug(
"%s: Successfully removed icon from status bar", Q_FUNC_INFO);
94 qOhosPrintfDebug(
"%s", Q_FUNC_INFO);
96 if (iconData.IsEmpty()) {
97 qOhosPrintfError(
"%s: Icon data is empty", Q_FUNC_INFO);
102 "@kit.StatusBarExtensionKit.statusBarManager.updateStatusBarIcon(*)",
103 {getContextForStatusBarManager(jsState), iconData});
105 qOhosPrintfDebug(
"%s: Successfully updated status bar icon", Q_FUNC_INFO);
111 "@kit.StatusBarExtensionKit.statusBarManager.updateStatusBarMenu(*)",
112 {getContextForStatusBarManager(jsState), statusBarGroupMenus});
114 qOhosPrintfDebug(
"%s: Successfully updated status bar menu", Q_FUNC_INFO);
118 QtOhos::
JsState &jsState, QNapi::Object statusBarIcon,
const std::string &title,
119 QNapi::Array statusBarGroupMenus)
121 auto *env = jsState.env();
123 auto quickOperation = QNapi::makeObject(
127 {
"height", quickOperationHeight},
131 return QNapi::makeObject(
134 {
"icons", statusBarIcon},
135 {
"quickOperation", quickOperation},
136 {
"statusBarGroupMenu", statusBarGroupMenus},
142 constexpr auto fallbackDisplayDensity = 1.0;
144 auto primaryDisplay = jsState.eval<QNapi::Object>(
"@ohos.display.getPrimaryDisplaySync()");
145 auto displayInfo = QOhosDisplayInfo::makeFromOhosDisplayObject(jsState, primaryDisplay);
148 if (displayInfo.densityPixels > 0.0) {
149 density =
std::lround(displayInfo.densityPixels);
151 qOhosPrintfDebug(
"%s: invalid display densityPixels: %.3f", Q_FUNC_INFO, displayInfo.densityPixels);
155 return density > 0.0 ? density : fallbackDisplayDensity;
161 "%s: image dimensions: %dx%d, format: %d, bytes: %lld",
162 Q_FUNC_INFO, image.width(), image.height(),
static_cast<
int>(image.format()), image.sizeInBytes());
164 const int sourceWidth = image.width();
165 const int sourceHeight = image.height();
169 const double widthVp =
std::lround(sourceWidth / density);
170 const double heightVp =
std::lround(sourceHeight / density);
172 QImage newImage = image.scaled(widthVp, heightVp, Qt::KeepAspectRatio, Qt::SmoothTransformation);
174 const int width = newImage.width();
175 const int height = newImage.height();
176 const double finalWidthVp = width / density;
177 const double finalHeightVp = height / density;
180 "%s: density=%.3f, vp %.2f x %.2f -> %.2f x %.2f, px %dx%d -> %dx%d",
181 Q_FUNC_INFO, density, widthVp, heightVp, finalWidthVp, finalHeightVp, sourceWidth, sourceHeight, width, height);
183 return createNapiPixelMapFromQImage(jsState, newImage);
187 const QIcon &icon,
const QSize &imageSize,
const QColor &fallbackContentColor)
189 auto iconImage = icon.pixmap(imageSize).toImage();
190 if (!iconImage.isNull())
193 auto fallbackImage = QImage(imageSize, QImage::Format_RGBA8888);
194 fallbackImage.fill(fallbackContentColor);
195 return fallbackImage;
200 QImage monochromeImage = sourceImage.copy();
201 const int colorValue = useWhite ? 255 : 0;
203 for (
int y = 0; y < monochromeImage.height(); ++y) {
204 for (
int x = 0; x < monochromeImage.width(); ++x) {
205 const QRgb pixel = monochromeImage.pixel(x, y);
206 const int alpha = qAlpha(pixel);
209 monochromeImage.setPixel(x, y, qRgba(colorValue, colorValue, colorValue, alpha));
213 return monochromeImage;
218 const QSize iconSize(48, 48);
220 QImage iconImage = icon.pixmap(iconSize).toImage();
221 if (iconImage.isNull()) {
222 QImage defaultImage(iconSize, QImage::Format_RGBA8888);
223 defaultImage.fill(isWhiteIcon ? Qt::white : Qt::black);
224 return makeDisplayDensityScaledJsPixelMapFromQImage(jsState, defaultImage);
228 "%s: original Icon dimensions: %dx%d, format: %d, bytes: %lld",
229 Q_FUNC_INFO, iconImage.width(), iconImage.height(),
230 static_cast<
int>(iconImage.format()), iconImage.sizeInBytes());
232 return makeDisplayDensityScaledJsPixelMapFromQImage(
233 jsState, convertToMonochromeIcon(iconImage, isWhiteIcon));
238 auto *env = jsState.env();
240 auto whitePixelMap = makeJsPixelMapFromIcon(jsState, icon,
true);
241 auto blackPixelMap = makeJsPixelMapFromIcon(jsState, icon,
false);
243 auto imageSize = whitePixelMap.eval<QNapi::Object>(
"getImageInfoSync().size");;
245 int imageWidth = imageSize.get<QNapi::Number>(
"width");
246 int imageHeight = imageSize.get<QNapi::Number>(
"height");
247 qOhosPrintfDebug(
"%s: ceated PixelMap size: %dx%d", Q_FUNC_INFO, imageWidth, imageHeight);
249 return QNapi::makeObject(
252 {
"white", whitePixelMap},
253 {
"black", blackPixelMap},
262 return QNapi::makeObject(
265 {
"notificationContentType", jsState.mapOhosEnumToJs(ContentType::NOTIFICATION_CONTENT_BASIC_TEXT)},
282 auto iconPixelMap = makeDisplayDensityScaledJsPixelMapFromQImage(
283 jsState, convertIconToScaledImage(icon, notificationIconSize, Qt::transparent));
284 auto notificationRequest = QNapi::makeObject(
287 {
"content", makeJsNotificationContent(jsState, title, content)},
288 {
"smallIcon", iconPixelMap},
290 if (optAutoDeletedDelayMs.hasValue()) {
291 notificationRequest.set(
293 QDateTime::currentMSecsSinceEpoch() + optAutoDeletedDelayMs.value());
296 return notificationRequest;
299class QOhosSystemTrayIcon
final :
public QPlatformSystemTrayIcon
312 const QString &title,
const QString &msg,
const QIcon &icon,
313 MessageIcon iconType,
int msecs)
override;
321 bool m_initialized =
false;
323 QPlatformMenu *m_menu =
nullptr;
327 std::shared_ptr<
void> m_iconLeftClickListenerHandle;
330 std::shared_ptr<JsScopeData> m_jsScopeData;
340void QOhosSystemTrayIcon::
init()
345 auto jsStatusBarGroupMenusFactory =
347 ?
static_cast<QOhosStatusBarMenu *>(m_menu)->makeJsStatusBarGroupMenusFactory()
348 : makeEmptyJsArrayFactory();
350 m_jsScopeData = QtOhos::makeProxyWithJsThreadDeleter(std::make_shared<JsScopeData>());
352 auto selfRef = QtOhos::makeQThreadSafeRef(
this);
355 auto jsStatusBarIcon = makeJsStatusBarIcon(jsState, m_icon);
356 auto jsStatusBarGroupMenus = jsStatusBarGroupMenusFactory(jsState);
357 auto jsStatusBarItem = makeJsStatusBarItem(
358 jsState, jsStatusBarIcon, ohosSystemTrayItemTitle, jsStatusBarGroupMenus);
359 addItemToOhosStatusBar(jsState, jsStatusBarItem);
360 m_jsScopeData->m_iconLeftClickListenerHandle = registerOhosIconLeftClickListener(
363 selfRef.visitInQtThreadIfAlive(
365 Q_EMIT self.activated(QPlatformSystemTrayIcon::Trigger);
370 m_initialized =
true;
372 qOhosPrintfDebug(
"%s: System tray icon initialized", Q_FUNC_INFO);
382 m_jsScopeData.reset();
384 m_initialized =
false;
396 updateOhosStatusBarIcon(jsState, makeJsStatusBarIcon(jsState, icon));
407 return trayIconGeometry;
411 const QString &title,
const QString &msg,
const QIcon &icon, MessageIcon iconType,
int msecs)
416 auto notificationRequest = makeJsNotificationRequest(
417 jsState, title.toStdString(), msg.toStdString(), icon,
418 msecs > 0 ? makeQOhosOptional(msecs) : makeEmptyQOhosOptional());
419 jsState.eval<QNapi::Promise>(
"@ohos.notificationManager.publish(*)", {notificationRequest})
420 .onCatch(QtOhos::makeErrorLoggingJsCallback(
"publish()"))
421 .onFinally(std::move(continueFunc));
437 if (m_menu ==
nullptr)
438 return makeQOhosStatusBarMenu().release();
444 qOhosPrintfDebug(
"%s: Updating status bar menu", Q_FUNC_INFO);
448 if (!m_initialized) {
449 qOhosPrintfDebug(
"%s: System tray not initialized yet", Q_FUNC_INFO);
453 auto jsStatusBarGroupMenusFactory =
455 ?
static_cast<QOhosStatusBarMenu *>(m_menu)->makeJsStatusBarGroupMenusFactory()
456 : makeEmptyJsArrayFactory();
460 updateOhosStatusBarMenu(jsState, jsStatusBarGroupMenusFactory(jsState));
468 return std::make_unique<QOhosSystemTrayIcon>();
std::enable_if_t< qohosplugincore_h_detail::isQOhosOptional< QOhosInvokeResult< Func, T > >, QOhosInvokeResult< Func, T > > andThen(Func &&func) const
void init() override
This method is called to initialize the platform dependent implementation.
void cleanup() override
This method is called to cleanup the platform dependent implementation.
void showMessage(const QString &title, const QString &msg, const QIcon &icon, MessageIcon iconType, int msecs) override
Shows a balloon message for the entry with the given title, message msg and icon for the time specifi...
~QOhosSystemTrayIcon() override
void updateMenu(QPlatformMenu *menu) override
This method is called when the system tray menu did change.
QPlatformMenu * createMenu() const override
This method allows platforms to use a different QPlatformMenu for system tray menus than what would n...
QRect geometry() const override
This method returns the geometry of the platform dependent system tray icon on the screen.
bool supportsMessages() const override
Returns true if the system tray supports messages on the platform.
bool isSystemTrayAvailable() const override
Returns true if the system tray is available on the platform.
void updateToolTip(const QString &tooltip) override
This method is called when the tooltip text did change.
void updateIcon(const QIcon &icon) override
This method is called when the icon did change.
virtual std::shared_ptr< QAbilityPeer > defaultQAbilityPeer()=0
static std::shared_ptr< QUiAbilityPeer > tryCastFromQAbilityPeerOrNull(std::shared_ptr< QAbilityPeer > qAbilityPeer)
QImage convertToMonochromeIcon(const QImage &sourceImage, bool useWhite)
const auto notificationIconSize
QNapi::Object makeJsStatusBarIcon(QtOhos::JsState &jsState, const QIcon &icon)
QImage convertIconToScaledImage(const QIcon &icon, const QSize &imageSize, const QColor &fallbackContentColor)
void addItemToOhosStatusBar(QtOhos::JsState &jsState, QNapi::Object statusBarItem)
QNapi::Object getContextForStatusBarManager(QtOhos::JsState &jsState)
QNapi::Object makeDisplayDensityScaledJsPixelMapFromQImage(QtOhos::JsState &jsState, const QImage &image)
QNapi::Object makeJsNotificationContent(QtOhos::JsState &jsState, const std::string &title, const std::string &text)
std::function< QNapi::Array(QtOhos::JsState &)> makeEmptyJsArrayFactory()
void removeIconFromOhosStatusBar(QtOhos::JsState &jsState)
QNapi::Object makeJsNotificationRequest(QtOhos::JsState &jsState, const std::string &title, const std::string &content, const QIcon &icon, QOhosOptional< int > optAutoDeletedDelayMs)
void updateOhosStatusBarIcon(QtOhos::JsState &jsState, QNapi::Object iconData)
const auto trayIconGeometry
constexpr int quickOperationHeight
QNapi::Object makeJsStatusBarItem(QtOhos::JsState &jsState, QNapi::Object statusBarIcon, const std::string &title, QNapi::Array statusBarGroupMenus)
std::shared_ptr< void > registerOhosIconLeftClickListener(QtOhos::JsState &jsState, std::function< void()> leftClickListener)
const std::string ohosSystemTrayItemTitle
QNapi::Object makeJsPixelMapFromIcon(QtOhos::JsState &jsState, const QIcon &icon, bool isWhiteIcon)
double getPrimaryDisplayPixelDensity(QtOhos::JsState &jsState)
void updateOhosStatusBarMenu(QtOhos::JsState &jsState, QNapi::Array statusBarGroupMenus)
void runInJsThreadAndWait(const std::function< void(JsState &)> &task)
void invokeInJsThreadAndWaitForContinue(std::function< void(JsState &, std::function< void()>)> &&task)
QT_BEGIN_NAMESPACE std::unique_ptr< QPlatformSystemTrayIcon > makeQOhosSystemTrayIcon()