Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosabilitycontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtHarmonyExtras/private/qohosenums_p.h>
7#include <QtHarmonyExtras/private/qohosjsenv_p.h>
8#include <QtHarmonyExtras/private/qohosoperationstatus_p.h>
9#include <QtHarmonyExtras/private/qohossharekit_p.h>
10#include <QtHarmonyExtras/private/qohosstartoptions_p.h>
11#include <QtHarmonyExtras/private/qohoswantinfo_p.h>
12#include <QtHarmonyExtras/private/qohoswantutils_p.h>
13
14#include <QtGui/qimage.h>
15#include <QtGui/qwindow.h>
16#include <QtGui/private/qohosimageconversions_p.h>
17
18#include <QtCore/qeventloop.h>
19#include <QtCore/qhash.h>
20#include <QtCore/qmap.h>
21#include <QtCore/qrandom.h>
22#include <QtCore/qsharedpointer.h>
23#include <QtCore/private/qcore_ohos_p.h>
24#include <QtCore/private/qohoscommon_p.h>
25#include <QtCore/private/qohoslogger_p.h>
26
27#include <array>
28#include <cstdint>
29#include <cstring>
30#include <functional>
31#include <map>
32#include <vector>
33
34QT_BEGIN_NAMESPACE
35
36namespace QtHarmonyExtras {
37
38using namespace Private;
39
40namespace {
41
42constexpr const char *qtOnContinueMigrationDataPropertyName = "__io_qt_on_continue_migration_data";
43
44QOhosSupplier<QByteArray> makeUniqueIdsGenerator()
45{
46 return [sequenceNumber = std::uint64_t(0)]() mutable {
47 QByteArray idBytes(32, Qt::Uninitialized);
48 std::memcpy(idBytes.data(), &sequenceNumber, sizeof(sequenceNumber));
49 QRandomGenerator::global()->generate(idBytes.begin() + sizeof(sequenceNumber), idBytes.end());
50 ++sequenceNumber;
51 return idBytes;
52 };
53}
54
55class QOhosOpenLinkOptionsImpl : public OpenLinkOptions
56{
57public:
58 QOhosOpenLinkOptionsImpl();
59
60 void setAppLinkingOnly(bool appLinkingOnly) override;
61
62 std::optional<bool> appLinkingOnly() const;
63
64private:
65 std::optional<bool> m_appLinkingOnly;
66};
67
68QOhosOpenLinkOptionsImpl::QOhosOpenLinkOptionsImpl() = default;
69
70void QOhosOpenLinkOptionsImpl::setAppLinkingOnly(bool appLinkingOnly)
71{
72 m_appLinkingOnly = appLinkingOnly;
73}
74
75std::optional<bool> QOhosOpenLinkOptionsImpl::appLinkingOnly() const
76{
77 return m_appLinkingOnly;
78}
79
80using OnContinueResult = QtOhos::enums::ohos::app::ability::AbilityConstant::OnContinueResult;
81
82enum class AbilityOnContinueResponseStatus
83{
84 Agree,
85 Reject,
86 Mismatch,
87};
88
89struct AbilityOnContinueRequest
90{
91 int sourceApplicationVersionCode;
92};
93
94struct AbilityOnContinueResponse
95{
96 AbilityOnContinueResponseStatus status;
97 QMap<QString, QString> wantObjectParams;
98 std::optional<bool> exitAppOnSourceDeviceAfterMigration;
99};
100
101std::optional<OnContinueResult> tryMapAbilityOnContinueResponseStatusToOhos(
102 AbilityOnContinueResponseStatus status)
103{
104 switch (status) {
105 case AbilityOnContinueResponseStatus::Agree:
106 return OnContinueResult::AGREE;
107 case AbilityOnContinueResponseStatus::Reject:
108 return OnContinueResult::REJECT;
109 case AbilityOnContinueResponseStatus::Mismatch:
110 return OnContinueResult::MISMATCH;
111 }
112 return std::nullopt;
113}
114
115void setOnContinueRequestsHandlerForAbilityInstanceWindow(
116 QObject *windowObject,
117 std::function<void(AbilityOnContinueRequest, QOhosConsumer<AbilityOnContinueResponse>)> requestsHandler)
118{
119 auto *qWindow = qobject_cast<QWindow *>(windowObject);
120 if (qWindow == nullptr)
121 qOhosReportFatalErrorAndAbort("%s: windowObject argument is null or not a QWindow", Q_FUNC_INFO);
122
123 auto qWindowRef = QtOhos::QObjectThreadSafeRef(qWindow);
124 auto sharedRequestsHandler = QtOhos::moveToSharedPtr(std::move(requestsHandler));
125
126 struct JsResultContext
127 {
128 QNapi::Reference<QNapi::Object> wantParamsReference;
129 QOhosConsumer<QOhosJsState &, QNapi::Number> resultConsumer;
130 };
131
132 QOhosJsThreadGateway::runAndWait(
133 [&](QOhosJsState &jsState) {
134 auto optQAbility = jsState.tryGetQAbilityByQWindow(qWindowRef);
135 if (!optQAbility) {
136 qOhosPrintfError(
137 "%s: no ability for window %s, handler not set",
138 Q_FUNC_INFO, qWindowRef.refName().c_str());
139 return;
140 }
141
142 jsState.setOnContinueRequestsHandler(
143 optQAbility.value(),
144 [sharedRequestsHandler](QOhosJsState &, QNapi::Object wantParamsObj, auto resultConsumer) {
145 int sourceVersionCode = wantParamsObj.get<QNapi::Number>("version");
146 auto jsResultContext = QtOhos::makeProxyWithJsThreadDeleter(std::make_shared<JsResultContext>());
147 jsResultContext->wantParamsReference = QNapi::Reference<>::makePersistentFrom(wantParamsObj);
148 jsResultContext->resultConsumer = std::move(resultConsumer);
149 QtOhos::invokeInQtThread(
150 [sharedRequestsHandler, sourceVersionCode, jsResultContext]() {
151 (*sharedRequestsHandler)(
152 AbilityOnContinueRequest{
153 .sourceApplicationVersionCode = sourceVersionCode,
154 },
155 [jsResultContext](AbilityOnContinueResponse qtResponse) {
156 QOhosJsThreadGateway::invoke(
157 [jsResultContext, qtResponse](QOhosJsState &jsState) {
158 if (qtResponse.status == AbilityOnContinueResponseStatus::Agree) {
159 auto wantParamsObj = jsResultContext->wantParamsReference.Value();
160 auto newWantParamsIter = qtResponse.wantObjectParams.constKeyValueBegin();
161 while (newWantParamsIter != qtResponse.wantObjectParams.constKeyValueEnd()) {
162 wantParamsObj.set(
163 newWantParamsIter->first.toStdString(),
164 newWantParamsIter->second.toStdString());
165 ++newWantParamsIter;
166 }
167 if (qtResponse.exitAppOnSourceDeviceAfterMigration.has_value()) {
168 wantParamsObj.set(
169 jsState.eval<QNapi::String>(
170 "@ohos.app.ability.wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY"),
171 qtResponse.exitAppOnSourceDeviceAfterMigration.value());
172 }
173 }
174 auto optResultEnum = tryMapAbilityOnContinueResponseStatusToOhos(qtResponse.status);
175 if (!optResultEnum) {
176 qOhosPrintfWarning(
177 "%s: got illegal status (%d) from requests handler, rejecting the request",
178 Q_FUNC_INFO, static_cast<int>(qtResponse.status));
179 }
180 jsResultContext->resultConsumer(
181 jsState, jsState.mapOhosEnumToJs(optResultEnum.value_or(OnContinueResult::REJECT)));
182 });
183 });
184 });
185 });
186 },
187 Q_FUNC_INFO);
188}
189
190void setDestroyAllowedFlagForAbilityInstances(
191 std::vector<QObject *> instancesMainWindows, bool destroyEnabled)
192{
193 std::vector<QtOhos::QObjectThreadSafeRef> instancesMainWindowsRefs;
194 for (auto *instanceMainWindow : instancesMainWindows)
195 instancesMainWindowsRefs.emplace_back(instanceMainWindow);
196
197 QOhosJsThreadGateway::runAndWait(
198 [&](QOhosJsState &jsState) {
199 for (const auto &instanceMainWindowRef : instancesMainWindowsRefs) {
200 auto optQAbility = jsState.tryGetQAbilityByQWindow(instanceMainWindowRef);
201 if (optQAbility)
202 jsState.setDestroyFromSystemAllowed(optQAbility.value(), destroyEnabled);
203 }
204 },
205 Q_FUNC_INFO);
206}
207
208class QOhosOnContinueContextImpl : public OnContinueContext
209{
210public:
211 QOhosOnContinueContextImpl(int sourceApplicationVersionCode);
212
213 void sendAgreeResponse(const QByteArray &responseData) override;
214 void sendRejectResponse() override;
215 void sendMismatchResponse() override;
216
217 void setExitAppOnSourceDeviceAfterMigration(bool exitAfterMigration) override;
218
219 int sourceApplicationVersionCode() const override;
220
221 AbilityOnContinueResponse response() const;
222
223private:
224 int m_sourceApplicationVersionCode;
225 AbilityOnContinueResponse m_baseResponse;
226 std::optional<bool> m_exitAppOnSourceDeviceAfterMigration;
227};
228
229QOhosOnContinueContextImpl::QOhosOnContinueContextImpl(int sourceApplicationVersionCode)
230 : OnContinueContext()
231 , m_sourceApplicationVersionCode(sourceApplicationVersionCode)
232{
233 sendRejectResponse();
234}
235
236/*!
237 \fn void QtHarmonyExtras::OnContinueContext::sendAgreeResponse(const QByteArray &responseData)
238
239 Sends an agree response to the OnContinue request, carrying \a responseData.
240 Agreeing accepts the continuation process.
241 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V13/js-apis-app-ability-abilityconstant-V13#oncontinueresult}
242 {OnContinueResult}
243*/
244void QOhosOnContinueContextImpl::sendAgreeResponse(const QByteArray &responseData)
245{
246 m_baseResponse = AbilityOnContinueResponse{
247 .status = AbilityOnContinueResponseStatus::Agree,
248 .wantObjectParams = {
249 {
250 QString::fromUtf8(qtOnContinueMigrationDataPropertyName),
251 QString::fromUtf8(responseData.toBase64())
252 },
253 },
254 .exitAppOnSourceDeviceAfterMigration = {},
255 };
256}
257
258/*!
259 \fn void QtHarmonyExtras::OnContinueContext::sendRejectResponse()
260
261 Sends a reject response to the OnContinue request.
262 Rejecting means the continuation process should not be continued.
263 This is typically used when the target application cannot handle the continuation
264 request for any reason.
265 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V13/js-apis-app-ability-abilityconstant-V13#oncontinueresult}
266 {OnContinueResult}
267*/
268void QOhosOnContinueContextImpl::sendRejectResponse()
269{
270 m_baseResponse = AbilityOnContinueResponse{
271 .status = AbilityOnContinueResponseStatus::Reject,
272 .wantObjectParams = {},
273 .exitAppOnSourceDeviceAfterMigration = {},
274 };
275}
276
277/*!
278 \fn void OnContinueContext::sendMismatchResponse()
279
280 Sends a mismatch response to the OnContinue request.
281 A mismatch means the continuation process should not be continued - most probably due to
282 source and target application version code difference.
283 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V13/js-apis-app-ability-abilityconstant-V13#oncontinueresult}
284 {OnContinueResult}
285
286 \sa sourceApplicationVersionCode()
287*/
288void QOhosOnContinueContextImpl::sendMismatchResponse()
289{
290 m_baseResponse = AbilityOnContinueResponse{
291 .status = AbilityOnContinueResponseStatus::Mismatch,
292 .wantObjectParams = {},
293 .exitAppOnSourceDeviceAfterMigration = {},
294 };
295}
296
297/*!
298 \fn void OnContinueContext::setExitAppOnSourceDeviceAfterMigration(bool exitAfterMigration)
299
300 Decides whether the application should automatically exit on the source device after successful
301 migration to the target device.
302
303 The default setting is determined by the platform. As of API17 it's set to "true" by default.
304*/
305void QOhosOnContinueContextImpl::setExitAppOnSourceDeviceAfterMigration(bool exitAfterMigration)
306{
307 m_exitAppOnSourceDeviceAfterMigration = exitAfterMigration;
308}
309
310AbilityOnContinueResponse QOhosOnContinueContextImpl::response() const
311{
312 auto response = m_baseResponse;
313 response.exitAppOnSourceDeviceAfterMigration = m_exitAppOnSourceDeviceAfterMigration;
314 return response;
315}
316
317/*!
318 \fn int QtHarmonyExtras::OnContinueContext::sourceApplicationVersionCode() const
319
320 Returns source application version code. Source application is the one which has started
321 the continuation process. This version code is devlivered as "version" property in
322 onContinue wantParam.
323*/
324int QOhosOnContinueContextImpl::sourceApplicationVersionCode() const
325{
326 return m_sourceApplicationVersionCode;
327}
328
329std::optional<QtOhos::enums::ohos::app::ability::AbilityConstant::WindowMode> tryMapWindowModeToOhosOrLogWarning(
330 QOhosStartOptionsData::WindowMode windowMode)
331{
332 namespace AbilityConstant = QtOhos::enums::ohos::app::ability::AbilityConstant;
333 using OptionsData = QOhosStartOptionsData;
334
335 switch (windowMode) {
336 case OptionsData::WindowMode::WINDOW_MODE_SPLIT_PRIMARY:
337 return std::make_optional(AbilityConstant::WindowMode::WINDOW_MODE_SPLIT_PRIMARY);
338 case OptionsData::WindowMode::WINDOW_MODE_SPLIT_SECONDARY:
339 return std::make_optional(AbilityConstant::WindowMode::WINDOW_MODE_SPLIT_SECONDARY);
340 case OptionsData::WindowMode::WINDOW_MODE_FULLSCREEN:
341 return std::make_optional(AbilityConstant::WindowMode::WINDOW_MODE_FULLSCREEN);
342 }
343
344 qCWarning(QtForOhos, "%s: got illegal WindowMode: %d", Q_FUNC_INFO, static_cast<int>(windowMode));
345
346 return {};
347}
348
349std::optional<QtOhos::enums::ohos::app::ability::contextConstant::ProcessMode> tryMapProcessModeToOhosOrLogWarning(
350 QOhosStartOptionsData::ProcessMode processMode)
351{
352 namespace contextConstant = QtOhos::enums::ohos::app::ability::contextConstant;
353 using OptionsData = QOhosStartOptionsData;
354
355 switch (processMode) {
356 case OptionsData::ProcessMode::NEW_PROCESS_ATTACH_TO_PARENT:
357 return std::make_optional(contextConstant::ProcessMode::NEW_PROCESS_ATTACH_TO_PARENT);
358 case OptionsData::ProcessMode::NEW_PROCESS_ATTACH_TO_STATUS_BAR_ITEM:
359 return std::make_optional(contextConstant::ProcessMode::NEW_PROCESS_ATTACH_TO_STATUS_BAR_ITEM);
360 }
361
362 qCWarning(QtForOhos, "%s: got illegal ProcessMode: %d", Q_FUNC_INFO, static_cast<int>(processMode));
363
364 return {};
365}
366
367std::optional<QtOhos::enums::ohos::app::ability::contextConstant::StartupVisibility> tryMapStartupVisibilityToOhosOrLogWarning(
368 QOhosStartOptionsData::StartupVisibility startupVisibility)
369{
370 namespace contextConstant = QtOhos::enums::ohos::app::ability::contextConstant;
371 using OptionsData = QOhosStartOptionsData;
372
373 switch (startupVisibility) {
374 case OptionsData::StartupVisibility::STARTUP_HIDE:
375 return std::make_optional(contextConstant::StartupVisibility::STARTUP_HIDE);
376 case OptionsData::StartupVisibility::STARTUP_SHOW:
377 return std::make_optional(contextConstant::StartupVisibility::STARTUP_SHOW);
378 }
379
380 qCWarning(QtForOhos, "%s: got illegal StartupVisibility: %d", Q_FUNC_INFO, static_cast<int>(startupVisibility));
381
382 return {};
383}
384
385std::optional<QtOhos::enums::ohos::bundle::bundleManager::SupportWindowMode> tryMapSupportWindowModeToOhosOrLogWarning(
386 QOhosStartOptionsData::SupportWindowMode supportWindowMode)
387{
388 namespace bundleManager = QtOhos::enums::ohos::bundle::bundleManager;
389 using OptionsData = QOhosStartOptionsData;
390
391 switch (supportWindowMode) {
392 case OptionsData::SupportWindowMode::FULL_SCREEN:
393 return std::make_optional(bundleManager::SupportWindowMode::FULL_SCREEN);
394 case OptionsData::SupportWindowMode::SPLIT:
395 return std::make_optional(bundleManager::SupportWindowMode::SPLIT);
396 case OptionsData::SupportWindowMode::FLOATING:
397 return std::make_optional(bundleManager::SupportWindowMode::FLOATING);
398 }
399
400 qCWarning(QtForOhos, "%s: got illegal SupportWindowMode: %d", Q_FUNC_INFO, static_cast<int>(supportWindowMode));
401
402 return {};
403}
404
405QNapi::Array mapSupportWindowModesToJsEnumsArray(
406 QOhosJsState &jsState, const QList<QOhosStartOptionsData::SupportWindowMode> &supportWindowModes)
407{
408 std::vector<QNapi::ValueWrapper> jsSupportWindowModes;
409 for (auto supportWindowMode : supportWindowModes) {
410 auto optOhosSupportWindowMode = tryMapSupportWindowModeToOhosOrLogWarning(supportWindowMode);
411 if (optOhosSupportWindowMode.has_value())
412 jsSupportWindowModes.push_back(jsState.mapOhosEnumToJs(optOhosSupportWindowMode.value()));
413 }
414
415 return QNapi::makeArray(jsState.env(), jsSupportWindowModes);
416}
417
418QNapi::Object makeJsCompletionHandler(
419 QOhosJsState &jsState, std::shared_ptr<QOhosConsumer<bool, QJsonObject, QString>> qtThreadCompletionHandler)
420{
421 auto makeCompletionCallback = [qtThreadCompletionHandler](bool requestSuccess) {
422 return [qtThreadCompletionHandler, requestSuccess](const QNapi::CallbackInfo &cbInfo) {
423 QNapi::Object elementNameObj;
424 QNapi::String messageValue;
425 cbInfo.getLeadingArgs(Q_FUNC_INFO, elementNameObj, messageValue);
426
427 const QJsonObject elementName = QOhosJsEnv::fromNapiValue<QJsonObject>(elementNameObj);
428 const QString message = QString::fromStdString(messageValue);
429
430 QtOhos::invokeInQtThread(
431 [qtThreadCompletionHandler, requestSuccess, elementName, message]() {
432 (*qtThreadCompletionHandler)(requestSuccess, elementName, message);
433 });
434 };
435 };
436
437 return QNapi::makeObject(
438 jsState.env(),
439 {
440 {"onRequestSuccess", makeCompletionCallback(true)},
441 {"onRequestFailure", makeCompletionCallback(false)},
442 });
443}
444
445QNapi::Object convertStartOptionsToNapiObject(
446 QOhosJsState &jsState, const QOhosStartOptionsData &opts)
447{
448 auto *env = jsState.env();
449 auto napiOptions = QNapi::Object::New(env);
450
451 auto optOhosWindowMode = opts.windowMode.has_value()
452 ? tryMapWindowModeToOhosOrLogWarning(opts.windowMode.value())
453 : std::nullopt;
454 if (optOhosWindowMode.has_value())
455 napiOptions.set("windowMode", jsState.mapOhosEnumToJs(optOhosWindowMode.value()));
456 if (opts.displayId.has_value())
457 napiOptions.set("displayId", opts.displayId.value());
458 if (opts.withAnimation.has_value())
459 napiOptions.set("withAnimation", opts.withAnimation.value());
460 if (opts.windowLeft.has_value())
461 napiOptions.set("windowLeft", opts.windowLeft.value());
462 if (opts.windowTop.has_value())
463 napiOptions.set("windowTop", opts.windowTop.value());
464 if (opts.windowWidth.has_value())
465 napiOptions.set("windowWidth", opts.windowWidth.value());
466 if (opts.windowHeight.has_value())
467 napiOptions.set("windowHeight", opts.windowHeight.value());
468 auto optOhosProcessMode = opts.processMode.has_value()
469 ? tryMapProcessModeToOhosOrLogWarning(opts.processMode.value())
470 : std::nullopt;
471 if (optOhosProcessMode.has_value())
472 napiOptions.set("processMode", jsState.mapOhosEnumToJs(optOhosProcessMode.value()));
473 auto optOhosStartupVisibility = opts.startupVisibility.has_value()
474 ? tryMapStartupVisibilityToOhosOrLogWarning(opts.startupVisibility.value())
475 : std::nullopt;
476 if (optOhosStartupVisibility.has_value())
477 napiOptions.set("startupVisibility", jsState.mapOhosEnumToJs(optOhosStartupVisibility.value()));
478 if (opts.windowIcon.has_value()) {
479 auto windowIcon = opts.windowIcon.value().value<QImage>();
480 if (!windowIcon.isNull())
481 napiOptions.set("startWindowIcon", makeOhosNapiPixelMapFromQImage(jsState, windowIcon));
482 }
483 if (opts.windowBackgroundColorHex.has_value())
484 napiOptions.set("startWindowBackgroundColor", opts.windowBackgroundColorHex.value().toStdString());
485 if (opts.supportWindowModes.has_value()) {
486 auto jsSupportWindowModes = mapSupportWindowModesToJsEnumsArray(jsState, opts.supportWindowModes.value());
487 if (jsSupportWindowModes.Length() != 0)
488 napiOptions.set("supportWindowModes", jsSupportWindowModes);
489 else
490 qCWarning(QtForOhos, "%s: OHOS doesn't support empty supportWindowModes, skipping", Q_FUNC_INFO);
491 }
492 if (opts.minWindowWidth.has_value())
493 napiOptions.set("minWindowWidth", opts.minWindowWidth.value());
494 if (opts.minWindowHeight.has_value())
495 napiOptions.set("minWindowHeight", opts.minWindowHeight.value());
496 if (opts.maxWindowWidth.has_value())
497 napiOptions.set("maxWindowWidth", opts.maxWindowWidth.value());
498 if (opts.maxWindowHeight.has_value())
499 napiOptions.set("maxWindowHeight", opts.maxWindowHeight.value());
500 if (opts.optCompletionHandler)
501 napiOptions.set("completionHandler", makeJsCompletionHandler(jsState, opts.optCompletionHandler));
502 if (opts.hideStartWindow.has_value())
503 napiOptions.set("hideStartWindow", opts.hideStartWindow.value());
504 if (opts.windowCreateParams.has_value()) {
505 const auto &windowCreateParams = opts.windowCreateParams.value();
506 std::vector<std::pair<std::string, QNapi::ValueWrapper>> windowCreateParamsProps;
507 if (windowCreateParams.setWindowFadeInOutAnimation) {
508 windowCreateParamsProps.emplace_back(
509 "animationParams",
510 QNapi::makeObject(
511 env,
512 {
513 {
514 "type",
515 jsState.mapOhosEnumToJs(
516 QtOhos::enums::ohos::window::AnimationType::FADE_IN_OUT),
517 }
518 }));
519 }
520 napiOptions.set("windowCreateParams", QNapi::makeObject(env, windowCreateParamsProps));
521 }
522
523 return napiOptions;
524}
525
526void requestStartAbilityForResult(
527 const Want &want, std::optional<QOhosStartOptionsData> options,
528 QWindow *optInstanceMainWindow, QObject *callerContext,
529 std::function<void(std::optional<StartAbilityResult>)> resultCallback)
530{
531 struct Context
532 {
533 std::function<void(std::optional<StartAbilityResult>)> resultCallback;
534 QtOhos::QObjectThreadSafeRef resultConsumerQtContextRef;
535 };
536
537 auto context = QtOhos::moveToSharedPtr(
538 Context{
539 .resultCallback = std::move(resultCallback),
540 .resultConsumerQtContextRef = QtOhos::QObjectThreadSafeRef(callerContext),
541 });
542
543 auto resultConsumer = [context](std::optional<QOhosAbilityResult> optAbilityResult) {
544 if (optAbilityResult.has_value()) {
545 auto abilityResult = optAbilityResult.value();
546 auto want = abilityResult.want.has_value()
547 ? std::optional(convertWantFromJsonObject(abilityResult.want.value()))
548 : std::nullopt;
549 context->resultCallback(StartAbilityResult{abilityResult.resultCode, want});
550 } else {
551 context->resultCallback(std::nullopt);
552 }
553 };
554
555 auto wantJson = convertWantToJsonObject(want);
556 auto optInstanceMainWindowRef =
557 optInstanceMainWindow != nullptr
558 ? std::make_optional(QtOhos::QObjectThreadSafeRef(optInstanceMainWindow))
559 : std::nullopt;
560
561 QOhosJsThreadGateway::invoke(
562 [context, resultConsumer, wantJson, options, optInstanceMainWindowRef](QOhosJsState &jsState) {
563 auto optUiAbility = optInstanceMainWindowRef.has_value()
564 ? jsState.tryGetQAbilityByQWindow(optInstanceMainWindowRef.value())
565 : jsState.defaultQAbility();
566 if (!optUiAbility.has_value()) {
567 context->resultConsumerQtContextRef.visitInQtThreadIfAlive(
568 [resultConsumer](auto &) {
569 resultConsumer({});
570 });
571 return;
572 }
573
574 auto arguments = std::vector<QNapi::ValueWrapper>{QOhosJsEnv::toNapiValue(jsState.env(), wantJson)};
575 if (options.has_value())
576 arguments.push_back(convertStartOptionsToNapiObject(jsState, options.value()));
577
578 optUiAbility.value().evalToPromiseOrRejectOnThrow("context.startAbilityForResult(*)", arguments)
579 .onThen(
580 [context, resultConsumer](const QOhosCallbackInfo &cbInfo) {
581 QNapi::Object abilityResult = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
582 int resultCode = abilityResult.get<QNapi::Number>("resultCode");
583
584 auto wantOrEmpty = QNapi::getOptionalPropOrEmpty<QNapi::Object>(abilityResult, "want");
585 auto jsonWant = !wantOrEmpty.IsEmpty()
586 ? std::optional<QJsonObject>(QOhosJsEnv::fromNapiValue<QJsonObject>(wantOrEmpty))
587 : std::nullopt;
588
589 context->resultConsumerQtContextRef.visitInQtThreadIfAlive(
590 [resultConsumer, resultCode, jsonWant](auto &) {
591 constexpr int startedAbilityErrorResultCode = -1;
592 resultConsumer(
593 resultCode != startedAbilityErrorResultCode
594 ? std::optional<QOhosAbilityResult>({resultCode, jsonWant})
595 : std::nullopt);
596 });
597 })
598 .onCatch(
599 [context, resultConsumer](const QOhosCallbackInfo &cbInfo) {
600 QtOhos::logJsCallbackError(cbInfo, "Got error from startAbilityForResult()");
601 context->resultConsumerQtContextRef.visitInQtThreadIfAlive(
602 [resultConsumer](auto &) {
603 resultConsumer({});
604 });
605 });
606 });
607}
608
609class QOhosBaseAbilityContextImpl : public AbilityContext
610{
611protected:
612 QOhosBaseAbilityContextImpl();
613
614 QByteArray generateUniqueId();
615
616 void startAbilityForResultImpl(
617 const Want &want, QWindow *optInstanceMainWindow,
618 std::optional<QOhosStartOptionsData> qpaStartOptions,
619 QObject *context, std::function<void(std::optional<StartAbilityResult>)> callback);
620
621 void shareDataWithShareKitImpl(
622 QWindow *optMainWindow, const QList<std::shared_ptr<ShareKit::SharedRecord>> &records,
623 std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions,
624 QObject *context,
625 std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted,
626 std::function<void()> onPanelClosed);
627
628private:
629 QOhosSupplier<QByteArray> m_uniqueIdsGenerator;
630 QHash<QByteArray, std::shared_ptr<void>> m_shareDataRequestsHandles;
631};
632
633class QOhosDefaultAbilityContextImpl : public QOhosBaseAbilityContextImpl
634{
635public:
636 QOhosDefaultAbilityContextImpl();
637
638 void setDestroyFromSystemEnabled(bool destroyEnabled) override;
639
640 void startAbilityForResult(
641 const Want &want, std::shared_ptr<StartOptions> options, QObject *context,
642 std::function<void(std::optional<StartAbilityResult>)> callback) override;
643
644 void shareDataWithShareKit(
645 const QList<std::shared_ptr<ShareKit::SharedRecord>> &records,
646 std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions,
647 QObject *context,
648 std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted,
649 std::function<void()> onPanelClosed) override;
650
651 bool tryOpenLink(const QString &link, std::shared_ptr<OpenLinkOptions> options) override;
652
653 void setContinuationActive(bool continuationActive) override;
654};
655
656class QOhosAbilityContextImpl : public QOhosBaseAbilityContextImpl
657{
658public:
659 QOhosAbilityContextImpl(QWindow *instanceMainWindow);
660
661 void setDestroyFromSystemEnabled(bool destroyEnabled) override;
662
663 void startAbilityForResult(
664 const Want &want, std::shared_ptr<StartOptions> options, QObject *context,
665 std::function<void(std::optional<StartAbilityResult>)> callback) override;
666
667 void shareDataWithShareKit(
668 const QList<std::shared_ptr<ShareKit::SharedRecord>> &records,
669 std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions,
670 QObject *context,
671 std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted,
672 std::function<void()> onPanelClosed) override;
673
674 bool tryOpenLink(const QString &link, std::shared_ptr<OpenLinkOptions> options) override;
675
676 void setContinuationActive(bool continuationActive) override;
677
678private:
679 QPointer<QWindow> m_instanceMainWindow;
680};
681
682std::map<QWindow *, std::unique_ptr<QOhosAbilityContextImpl>> abilityContextsMap;
683
684QOhosBaseAbilityContextImpl::QOhosBaseAbilityContextImpl()
685 : m_uniqueIdsGenerator(makeUniqueIdsGenerator())
686{
687}
688
689QByteArray QOhosBaseAbilityContextImpl::generateUniqueId()
690{
691 return m_uniqueIdsGenerator();
692}
693
694void QOhosBaseAbilityContextImpl::startAbilityForResultImpl(
695 const Want &want, QWindow *optInstanceMainWindow,
696 std::optional<QOhosStartOptionsData> qpaStartOptions,
697 QObject *context, std::function<void(std::optional<StartAbilityResult>)> callback)
698{
699 requestStartAbilityForResult(
700 want, std::move(qpaStartOptions), optInstanceMainWindow, context,
701 std::move(callback));
702}
703
704void QOhosBaseAbilityContextImpl::shareDataWithShareKitImpl(
705 QWindow *optMainWindow, const QList<std::shared_ptr<ShareKit::SharedRecord>> &records,
706 std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions,
707 QObject *context,
708 std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted,
709 std::function<void()> onPanelClosed)
710{
711 auto selfPtr = QPointer<QOhosBaseAbilityContextImpl>(this);
712 auto contextPtr = QPointer<QObject>(context);
713 auto requestId = generateUniqueId();
714 auto requestHandle = shareData(
715 optMainWindow, records, controllerOptions,
716 [selfPtr, contextPtr, requestId, onPanelClosed]() {
717 if (!selfPtr.isNull())
718 selfPtr->m_shareDataRequestsHandles.remove(requestId);
719 if (!contextPtr.isNull() && onPanelClosed)
720 onPanelClosed();
721 },
722 [contextPtr, onShareCompleted](auto shareOperationResult) {
723 if (!contextPtr.isNull() && onShareCompleted)
724 onShareCompleted(shareOperationResult);
725 }
726 );
727 m_shareDataRequestsHandles.insert(requestId, requestHandle);
728}
729
730QOhosDefaultAbilityContextImpl::QOhosDefaultAbilityContextImpl() = default;
731
732void QOhosDefaultAbilityContextImpl::setDestroyFromSystemEnabled(bool destroyEnabled)
733{
734 qCDebug(QtForOhos, "%s: setting destroyEnabled=%d for all instances", Q_FUNC_INFO, destroyEnabled);
735
736 std::vector<QObject *> instancesMainWindows;
737 for (const auto &contextEntry : abilityContextsMap)
738 instancesMainWindows.push_back(contextEntry.first);
739
740 setDestroyAllowedFlagForAbilityInstances(instancesMainWindows, destroyEnabled);
741}
742
743void QOhosDefaultAbilityContextImpl::startAbilityForResult(
744 const Want &want, std::shared_ptr<StartOptions> options, QObject *context,
745 std::function<void(std::optional<StartAbilityResult>)> callback)
746{
747 startAbilityForResultImpl(
748 want, nullptr,
749 options ? tryConvertStartOptionsToQpaFunctionsStruct(*options) : std::nullopt,
750 context, std::move(callback));
751}
752
753void QOhosDefaultAbilityContextImpl::shareDataWithShareKit(
754 const QList<std::shared_ptr<ShareKit::SharedRecord>> &records,
755 std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions,
756 QObject *context,
757 std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted,
758 std::function<void()> onPanelClosed)
759{
760 shareDataWithShareKitImpl(
761 nullptr, records, controllerOptions, context,
762 std::move(onShareCompleted), std::move(onPanelClosed));
763}
764
765bool startAbilityByTypeImpl(const QString &appType, const QJsonObject &wantParameters)
766{
767 // The call result of "context.startAbilityByType" will be synced and returned.
768 // However, the started ability result won't be synced here.
769 return QOhosJsThreadGateway::evalWithPromise<bool>(
770 [&](QOhosJsState &jsState, QOhosTaskPromise<bool> evalPromise) {
771 auto optQAbility = jsState.defaultQAbility();
772 if (!optQAbility.has_value()) {
773 evalPromise(false);
774 return;
775 }
776
777 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
778 optQAbility.value().evalToPromiseOrRejectOnThrow(
779 "context.startAbilityByType(*)",
780 {
781 appType.toStdString(),
782 QOhosJsEnv::toNapiValue(jsState.env(), wantParameters),
783 QNapi::makeObject(
784 jsState.env(),
785 {
786 {
787 "onResult",
788 [](const QOhosCallbackInfo&) {
789 qOhosPrintfDebug("startAbilityByType: onResult called");
790 }
791 },
792 {
793 "onError",
794 [](const QOhosCallbackInfo &cbInfo) {
795 QtOhos::logJsCallbackError(cbInfo, "startAbilityByType: onError called");
796 }
797 }
798 })
799 })
800 .onThen(
801 [thenPromise = std::move(thenCatchPromises.first)](const QOhosCallbackInfo &) {
802 thenPromise(true);
803 })
804 .onCatch(
805 [catchPromise = std::move(thenCatchPromises.second)](const QOhosCallbackInfo &cbInfo) {
806 QtOhos::logJsCallbackError(cbInfo, "startAbilityByType: failed");
807 catchPromise(false);
808 });
809 },
810 Q_FUNC_INFO);
811}
812
813bool tryOpenLinkImpl(QObject *optInstanceMainWindow, const QString &link, std::optional<bool> appLinkingOnly)
814{
815 if (optInstanceMainWindow != nullptr && qobject_cast<QWindow *>(optInstanceMainWindow) == nullptr)
816 qOhosReportFatalErrorAndAbort("%s: the main window argument is not a QWindow", Q_FUNC_INFO);
817
818 auto optInstanceMainWindowRef = optInstanceMainWindow != nullptr
819 ? std::make_optional(QtOhos::QObjectThreadSafeRef(optInstanceMainWindow))
820 : std::nullopt;
821
822 return QOhosJsThreadGateway::evalWithPromise<bool>(
823 [&](QOhosJsState &jsState, QOhosTaskPromise<bool> evalPromise) {
824 auto optUiAbility = optInstanceMainWindowRef.has_value()
825 ? jsState.tryGetQAbilityByQWindow(optInstanceMainWindowRef.value())
826 : jsState.defaultQAbility();
827 if (!optUiAbility.has_value()) {
828 evalPromise(false);
829 return;
830 }
831
832 std::vector<std::pair<std::string, QNapi::ValueWrapper>> openLinkOptions;
833 if (appLinkingOnly.has_value())
834 openLinkOptions.emplace_back("appLinkingOnly", appLinkingOnly.value());
835
836 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
837 optUiAbility.value().evalToPromiseOrRejectOnThrow(
838 "context.openLink(*)",
839 {
840 link.toStdString(),
841 QNapi::makeObject(jsState.env(), openLinkOptions),
842 })
843 .onThen(
844 [thenPromise = std::move(thenCatchPromises.first)](const QOhosCallbackInfo &) {
845 thenPromise(true);
846 })
847 .onCatch(
848 [catchPromise = std::move(thenCatchPromises.second)](const QOhosCallbackInfo &cbInfo) {
849 QtOhos::logJsCallbackError(cbInfo, "Got error from openLink()");
850 catchPromise(false);
851 });
852 },
853 Q_FUNC_INFO);
854}
855
856void setContinuationActiveImpl(QObject *optInstanceMainWindow, bool continuationActive)
857{
858 using ContinueState = QtOhos::enums::ohos::app::ability::AbilityConstant::ContinueState;
859
860 auto optInstanceMainWindowRef = optInstanceMainWindow != nullptr
861 ? std::make_optional(QtOhos::QObjectThreadSafeRef(optInstanceMainWindow))
862 : std::nullopt;
863
864 QOhosJsThreadGateway::invokeAndWaitForContinue(
865 [&](QOhosJsState &jsState, QOhosTaskPromise<> taskPromise) {
866 auto optUiAbility = optInstanceMainWindowRef.has_value()
867 ? jsState.tryGetQAbilityByQWindow(optInstanceMainWindowRef.value())
868 : jsState.defaultQAbility();
869 if (!optUiAbility.has_value()) {
870 taskPromise();
871 return;
872 }
873
874 auto continueState = continuationActive ? ContinueState::ACTIVE : ContinueState::INACTIVE;
875 optUiAbility.value().evalToPromiseOrRejectOnThrow(
876 "context.setMissionContinueState(*)", {jsState.mapOhosEnumToJs(continueState)})
877 .onCatch(QtOhos::makeErrorLoggingJsCallback("setMissionContinueState()"))
878 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
879 },
880 Q_FUNC_INFO);
881}
882
883bool QOhosDefaultAbilityContextImpl::tryOpenLink(const QString &link, std::shared_ptr<OpenLinkOptions> options)
884{
885 return tryOpenLinkImpl(
886 nullptr, link,
887 options
888 ? static_cast<const QOhosOpenLinkOptionsImpl &>(*options).appLinkingOnly()
889 : std::nullopt);
890}
891
892void QOhosDefaultAbilityContextImpl::setContinuationActive(bool continuationActive)
893{
894 setContinuationActiveImpl(nullptr, continuationActive);
895}
896
897QOhosAbilityContextImpl::QOhosAbilityContextImpl(QWindow *instanceMainWindow)
898 : m_instanceMainWindow(instanceMainWindow)
899{
900 setOnContinueRequestsHandlerForAbilityInstanceWindow(
901 instanceMainWindow,
902 [self = QPointer<QOhosAbilityContextImpl>(this)](auto request, auto responseConsumer) {
903 auto context = std::make_shared<QOhosOnContinueContextImpl>(request.sourceApplicationVersionCode);
904
905 if (!self.isNull())
906 Q_EMIT self->continueRequestReceived(context);
907 else
908 context->sendRejectResponse();
909
910 QtOhos::invokeInQtThread(
911 [response = context->response(), responseConsumer = std::move(responseConsumer)]() {
912 responseConsumer(response);
913 });
914 });
915}
916
917/*!
918 \fn void QtHarmonyExtras::AbilityContext::setDestroyFromSystemEnabled(bool destroyEnabled)
919
920 Sets whether the Ability can be automatically destroyed by the system when the user clicks on
921 the window's "close" button. If \a destroyEnabled is \c true, the system destroys the Ability
922 automatically (and Qt needs to adapt to this). If \a destroyEnabled = \c false, the window is
923 not automatically destroyed, but instead standard Qt path for window close is triggered, i.e.
924 QWindow::close().
925
926 By default, the flag is set to \c false.
927
928 When called on the default AbilityContext instance it sets the flag to \a destroyEnabled
929 for all Ability instances.
930*/
931void QOhosAbilityContextImpl::setDestroyFromSystemEnabled(bool destroyEnabled)
932{
933 if (m_instanceMainWindow.isNull()) {
934 qCWarning(QtForOhos, "%s: called on destroyed instance, ignoring", Q_FUNC_INFO);
935 return;
936 }
937
938 qCDebug(
939 QtForOhos, "%s: setting destroyEnabled=%d for window %p",
940 Q_FUNC_INFO, destroyEnabled, m_instanceMainWindow.data());
941
942 setDestroyAllowedFlagForAbilityInstances(
943 {m_instanceMainWindow.data()}, destroyEnabled);
944}
945
946/*!
947 \fn void QtHarmonyExtras::AbilityContext::startAbilityForResult(const QtHarmonyExtras::Want &want, std::shared_ptr<QtHarmonyExtras::StartOptions> options, QObject *context, std::function<void(std::optional<QtHarmonyExtras::StartAbilityResult>)> callback)
948
949 Starts a UIAbility with a given \a want and \a options and delivers the result by invoking \a callback on the thread of \a context;
950 if \a context is destroyed before the result arrives, \a callback is not invoked. To start the UIAbility, at least bundleName and abilityName properties must be set.
951 Pass a null \a options to start without any start options.
952 On success \a callback receives the ability result; on failure (for example, the UIAbility was killed) it receives an empty std::optional.
953 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references/js-apis-inner-application-uiabilitycontext#startabilityforresult-2}
954 {UIAbilityContext.startAbilityForResult}
955*/
956void QOhosAbilityContextImpl::startAbilityForResult(
957 const Want &want, std::shared_ptr<StartOptions> options, QObject *context,
958 std::function<void(std::optional<StartAbilityResult>)> callback)
959{
960 startAbilityForResultImpl(
961 want, m_instanceMainWindow,
962 options ? tryConvertStartOptionsToQpaFunctionsStruct(*options) : std::nullopt,
963 context, std::move(callback));
964}
965
966/*!
967 \fn virtual void QtHarmonyExtras::AbilityContext::shareDataWithShareKit(const QList<std::shared_ptr<ShareKit::SharedRecord>> &records, std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions, QObject *context, std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted, std::function<void()> onPanelClosed) = 0
968
969 Share provided \a records with other applications using an inter-application mechanism called ShareKit. Share Kit panel can be controlled
970 with a given \a controllerOptions. When called on the default AbilityContext instance, it shares \a records using the default UiAbility.
971
972 Results are delivered by invoking the callbacks on the thread of \a context; if \a context is destroyed the callbacks are not invoked.
973 \a onShareCompleted is called when the User selects an application for sharing (can be called multiple times).
974 \a onPanelClosed, if set, is called when the sharing panel is closed.
975
976 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V5/share-introduction-V5}{Share Kit}
977*/
978void QOhosAbilityContextImpl::shareDataWithShareKit(
979 const QList<std::shared_ptr<ShareKit::SharedRecord>> &records,
980 std::shared_ptr<ShareKit::ShareControllerOptions> controllerOptions,
981 QObject *context,
982 std::function<void(std::shared_ptr<ShareKit::ShareOperationResult>)> onShareCompleted,
983 std::function<void()> onPanelClosed)
984{
985 shareDataWithShareKitImpl(
986 m_instanceMainWindow, records, controllerOptions, context,
987 std::move(onShareCompleted), std::move(onPanelClosed));
988}
989
990/*!
991 \fn virtual bool QtHarmonyExtras::AbilityContext::tryOpenLink(const QString &link, std::shared_ptr<OpenLinkOptions> options) = 0
992
993 Use provided \a link and \a options to open application with a deep link; pass a null \a options
994 to open without any options.
995 Returns true if successful, false otherwise.
996
997 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-guides/app-linking-startup}{App Linking}
998*/
999bool QOhosAbilityContextImpl::tryOpenLink(const QString &link, std::shared_ptr<OpenLinkOptions> options)
1000{
1001 return tryOpenLinkImpl(
1002 m_instanceMainWindow, link,
1003 options
1004 ? static_cast<const QOhosOpenLinkOptionsImpl &>(*options).appLinkingOnly()
1005 : std::nullopt);
1006}
1007
1008/*!
1009 \fn virtual void QtHarmonyExtras::AbilityContext::setContinuationActive(bool continuationActive)
1010
1011 Sets the mission continuation state of the underlying Ability instance as per the
1012 \a continuationActive parameter (\c true = active, \c false = inactive).
1013
1014 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references/js-apis-inner-application-uiabilitycontext#setmissioncontinuestate10-1}{setMissionContinueState}
1015*/
1016void QOhosAbilityContextImpl::setContinuationActive(bool continuationActive)
1017{
1018 setContinuationActiveImpl(m_instanceMainWindow, continuationActive);
1019}
1020
1021std::shared_ptr<OperationStatus> startAbilityImpl(
1022 const Want &want, std::optional<QOhosStartOptionsData> qpaStartOptions)
1023{
1024 auto wantJson = convertWantToJsonObject(want);
1025 bool success = QOhosJsThreadGateway::eval(
1026 [&](QOhosJsState &jsState) {
1027 auto optMainUiAbility = jsState.defaultQAbility();
1028 if (!optMainUiAbility.has_value())
1029 return false;
1030 auto mainUiAbility = optMainUiAbility.value();
1031 if (mainUiAbility.IsEmpty())
1032 return false;
1033
1034 auto arguments = std::vector<QNapi::ValueWrapper>{QOhosJsEnv::toNapiValue(jsState.env(), wantJson)};
1035 if (qpaStartOptions.has_value())
1036 arguments.push_back(convertStartOptionsToNapiObject(jsState, qpaStartOptions.value()));
1037
1038 mainUiAbility.eval("context.startAbility(*)", arguments);
1039
1040 // FIXME:
1041 // * there should be error code taken from a call to JS `startAbility` function
1042 // * error code should be checked and provided to the returned `operationStatus`
1043 return true;
1044 },
1045 Q_FUNC_INFO);
1046 return createOperationStatus(success);
1047}
1048
1049void startAppProcessImpl(
1050 const QString &processId, const Want &requestWant,
1051 std::optional<QOhosStartOptionsData> qpaStartOptions)
1052{
1053 auto requestWantJson = convertWantToJsonObject(requestWant);
1054 QOhosJsThreadGateway::invokeAndWaitForContinue(
1055 [&](QOhosJsState &jsState, QOhosTaskPromise<> taskPromise) {
1056 auto startOptions = qpaStartOptions.has_value()
1057 ? convertStartOptionsToNapiObject(jsState, qpaStartOptions.value())
1058 : QNapi::Object();
1059
1060 auto sharedTaskPromise = QtOhos::moveToSharedPtr(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1061 jsState.startAppProcess(
1062 processId.toStdString(),
1063 QNapi::checkedCast<QNapi::Object>(
1064 QOhosJsEnv::toNapiValue(jsState.env(), requestWantJson)),
1065 startOptions,
1066 [sharedTaskPromise](QOhosJsState &) {
1067 (*sharedTaskPromise)();
1068 });
1069 },
1070 Q_FUNC_INFO);
1071}
1072
1073}
1074
1075/*!
1076 \class QtHarmonyExtras::AbilityContext
1077 \inmodule QtHarmonyExtras
1078 \since 5.12.12
1079 \brief The AbilityContext class is to manage native UI Ability context. See
1080 \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V5/js-apis-inner-application-uiabilitycontext-V5}
1081 {UIAbilityContext}.
1082*/
1083
1084/*!
1085 \fn void QtHarmonyExtras::AbilityContext::newWantInfoReceived(std::shared_ptr<QtHarmonyExtras::WantInfo> wantInfo)
1086
1087 Singal is emitted when an ability gets new \a want. See
1088 \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V5/js-apis-app-ability-uiability-V5#uiabilityonnewwant}
1089 {On New Want}.
1090*/
1091
1092/*!
1093 \fn void QtHarmonyExtras::AbilityContext::continueRequestReceived(std::shared_ptr<QtHarmonyExtras::OnContinueContext> onContinueContext)
1094
1095 Signal emitted when the ability receives onContinue request. The signal delivers \a onContinueContext on which User
1096 can set onContinue response.
1097 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V13/js-apis-app-ability-uiability-V13#uiabilityoncontinue}
1098 {UIAbility onContinue}.
1099
1100 \sa sendAgreeResponse(), sendMismatchResponse(), sendRejectResponse()
1101*/
1102
1111
1112AbilityContext::~AbilityContext() = default;
1113
1114/*!
1115 \fn AbilityContext *QtHarmonyExtras::AbilityContext::defaultInstance()
1116
1117 Returns instance of the class which is not connected to any specific Ability instance. It should
1118 be used when the application needs to perform some operations without selecting specific
1119 Ability instance (via the corresponding main window).
1120
1121 See descriptions of specific class methods for information about their behavior for the default
1122 instance.
1123
1124 The returned object is owned by QtHarmonyExtras and lives for the lifetime of the application;
1125 do not delete it.
1126*/
1132
1133/*!
1134 \fn AbilityContext *QtHarmonyExtras::AbilityContext::instanceForMainWindow(QWindow *instanceMainWindow)
1135
1136 Returns instance of the class which is connected to Ability instance identified by the
1137 \a instanceMainWindow. Methods called on the returned object will only affect the
1138 corresponding Ability instance.
1139
1140 \a instanceMainWindow must not be \c nullptr.
1141
1142 The returned object is owned by QtHarmonyExtras and remains valid while \a instanceMainWindow
1143 exists; do not delete it. Use QPointer to hold a reference that may outlive the window.
1144*/
1162
1163/*!
1164 \fn std::shared_ptr<OperationStatus> QtHarmonyExtras::startAbility(const Want &want, std::shared_ptr<StartOptions> options)
1165
1166 Starts an Ability for a given \a want and \a options; pass a null \a options to start without
1167 any start options. See
1168 \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V5/js-apis-inner-application-uiabilitycontext-V5#uiabilitycontextstartability-1}
1169 {Start Ability}.
1170
1171 \warning Currently, operation status result is hardcoded as "successful" (even if ability were
1172 not started).
1173*/
1174std::shared_ptr<OperationStatus> startAbility(const Want &want, std::shared_ptr<StartOptions> options)
1175{
1176 return startAbilityImpl(want, options ? tryConvertStartOptionsToQpaFunctionsStruct(*options) : std::nullopt);
1177}
1178
1179/*!
1180 \fn std::shared_ptr<OperationStatus> QtHarmonyExtras::startAbilityByType(const QString &appType,
1181 const QJsonObject &wantParameters)
1182
1183 Starts an Ability for a given \a appType and \a wantParameters. See
1184 \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V5/js-apis-inner-application-uiabilitycontext-V5#uiabilitycontextstartabilitybytype11-1}
1185 {Start Ability}.
1186
1187 \return true on success
1188*/
1189std::shared_ptr<OperationStatus> startAbilityByType(const QString &appType, const QJsonObject &wantParameters)
1190{
1191 bool success = startAbilityByTypeImpl(appType, wantParameters);
1192 return createOperationStatus(success);
1193}
1194
1195/*!
1196 Starts another instance of the UIAbility used by the Qt app with specified widget inside
1197
1198 The caller should pass newly created QWidget \a instanceWidget, without any setting any parent, calling show() or winId() on it.
1199*/
1200void startNewAbilityInstance(QWidget *instanceWidget)
1201{
1202 instanceWidget->show();
1203}
1204
1205/*!
1206 \fn void QtHarmonyExtras::startAppProcess(const QString &processId, const Want &requestWant, std::shared_ptr<StartOptions> options)
1207
1208 Starts application process for a given \a processId, \a requestWant and \a options; pass a null
1209 \a options to start without any start options.
1210*/
1211void startAppProcess(const QString &processId, const Want &requestWant, std::shared_ptr<StartOptions> options)
1212{
1213 startAppProcessImpl(processId, requestWant, options ? tryConvertStartOptionsToQpaFunctionsStruct(*options) : std::nullopt);
1214}
1215
1216/*!
1217 \fn void QtHarmonyExtras::setAbilityInstanceDestroyEnabled(QWindow *instanceWindow, bool destroyEnabled)
1218
1219 Sets whether the Ability related with \a instanceWindow can be automatically destroyed by the
1220 system when the user clicks on the window's "close" button. If \a destroyEnabled is \c true,
1221 the system destroys the Ability automatically (and Qt needs to adapt to this).
1222 If \a destroyEnabled = \c false, the window is not automatically destroyed, but instead standard
1223 Qt path for window close is triggered, i.e. QWindow::close().
1224
1225 By default, the flag is set to \c false.
1226
1227 \sa QtHarmonyExtras::AbilityContext::setDestroyFromSystemEnabled()
1228*/
1229void setAbilityInstanceDestroyEnabled(QWindow *instanceWindow, bool destroyEnabled)
1230{
1231 AbilityContext::instanceForMainWindow(instanceWindow)->setDestroyFromSystemEnabled(destroyEnabled);
1232}
1233
1234/*!
1235 \fn std::optional<QByteArray> tryGetOnContinueData(const Want &want)
1236
1237 Tries to get continuation / migration related data that was provided on the source device.
1238 Returns an empty optional if no such data found. The data is expected to be stored in the \a want parameters.
1239 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-guides/app-continuation-guide}
1240 {Application Continuation}.
1241*/
1243{
1244 auto key = QString::fromUtf8(qtOnContinueMigrationDataPropertyName);
1245 if (want.parameters.contains(key)) {
1246 auto base64String = want.parameters.value(key).toString();
1247 auto decodedData = QByteArray::fromBase64(base64String.toUtf8());
1248 return decodedData;
1249 } else {
1250 return {};
1251 }
1252}
1253
1254/*!
1255 \class QtHarmonyExtras::OnContinueContext
1256 \inmodule QtHarmonyExtras
1257 \since 5.12.12
1258 \brief The OnContinueContext class manages onContinue context. It provides system
1259 data, like source application version code and set the onContinue result that is requested by the system.
1260 See \l {https://developer.huawei.com/consumer/en/doc/harmonyos-references-V13/js-apis-app-ability-uiability-V13#uiabilityoncontinue}
1261 {UIAbility onContinue}.
1262*/
1265
1266OpenLinkOptions::OpenLinkOptions() = default;
1267OpenLinkOptions::~OpenLinkOptions() = default;
1268
1270{
1271 return std::make_shared<QOhosOpenLinkOptionsImpl>();
1272}
1273
1274}
1275
1276QT_END_NAMESPACE
void startAppProcess(const QString &processId, const Want &requestWant, std::shared_ptr< StartOptions > options)
Starts application process for a given processId, requestWant and options; pass a null options to sta...
std::shared_ptr< OpenLinkOptions > createOpenLinkOptions()
void startNewAbilityInstance(QWidget *instanceWidget)
Starts another instance of the UIAbility used by the Qt app with specified widget inside.
void setAbilityInstanceDestroyEnabled(QWindow *instanceWindow, bool destroyEnabled)
Sets whether the Ability related with instanceWindow can be automatically destroyed by the system whe...
std::shared_ptr< OperationStatus > startAbilityByType(const QString &appType, const QJsonObject &wantParameters)
Starts an Ability for a given appType and wantParameters.
std::optional< QByteArray > tryGetOnContinueData(const Want &want)
Tries to get continuation / migration related data that was provided on the source device.
std::shared_ptr< OperationStatus > startAbility(const Want &want, std::shared_ptr< StartOptions > options)
Starts an Ability for a given want and options; pass a null options to start without any start option...