summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/android/qandroidplatformiconengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/android/qandroidplatformiconengine.cpp')
-rw-r--r--src/plugins/platforms/android/qandroidplatformiconengine.cpp616
1 files changed, 616 insertions, 0 deletions
diff --git a/src/plugins/platforms/android/qandroidplatformiconengine.cpp b/src/plugins/platforms/android/qandroidplatformiconengine.cpp
new file mode 100644
index 0000000000..faa63dcca1
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformiconengine.cpp
@@ -0,0 +1,616 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qandroidplatformiconengine.h"
+#include "androidjnimain.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qjniarray.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qset.h>
+
+#include <QtGui/qfontdatabase.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpalette.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+Q_LOGGING_CATEGORY(lcIconEngineFontDownload, "qt.qpa.iconengine.fontdownload")
+
+// the primary types to work with the FontRequest API
+Q_DECLARE_JNI_CLASS(FontRequest, "androidx/core/provider/FontRequest")
+Q_DECLARE_JNI_CLASS(FontsContractCompat, "androidx/core/provider/FontsContractCompat")
+Q_DECLARE_JNI_CLASS(FontFamilyResult, "androidx/core/provider/FontsContractCompat$FontFamilyResult")
+Q_DECLARE_JNI_CLASS(FontInfo, "androidx/core/provider/FontsContractCompat$FontInfo")
+
+// various utility types
+Q_DECLARE_JNI_CLASS(List, "java/util/List"); // List is just an Interface
+Q_DECLARE_JNI_CLASS(ArrayList, "java/util/ArrayList");
+Q_DECLARE_JNI_CLASS(HashSet, "java/util/HashSet");
+Q_DECLARE_JNI_CLASS(Uri, "android/net/Uri")
+Q_DECLARE_JNI_CLASS(CancellationSignal, "android/os/CancellationSignal")
+Q_DECLARE_JNI_CLASS(ParcelFileDescriptor, "android/os/ParcelFileDescriptor")
+Q_DECLARE_JNI_CLASS(ContentResolver, "android/content/ContentResolver")
+Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager")
+Q_DECLARE_JNI_CLASS(ProviderInfo, "android/content/pm/ProviderInfo")
+Q_DECLARE_JNI_CLASS(PackageInfo, "android/content/pm/PackageInfo")
+Q_DECLARE_JNI_CLASS(Signature, "android/content/pm/Signature")
+
+namespace FontProvider {
+
+static QString fetchFont(const QString &query)
+{
+ using namespace QtJniTypes;
+
+ static QMap<QString, QString> triedFonts;
+ const auto it = triedFonts.find(query);
+ if (it != triedFonts.constEnd())
+ return it.value();
+
+ QString fontFamily;
+ triedFonts[query] = fontFamily; // mark as tried
+
+ QStringList loadedFamilies;
+ if (QFile file(query); file.open(QIODevice::ReadOnly)) {
+ qCDebug(lcIconEngineFontDownload) << "Loading font from resource" << query;
+ const QByteArray fontData = file.readAll();
+ int fontId = QFontDatabase::addApplicationFontFromData(fontData);
+ loadedFamilies << QFontDatabase::applicationFontFamilies(fontId);
+ } else if (!query.startsWith(u":/"_s)) {
+ const QString package = u"com.google.android.gms"_s;
+ const QString authority = u"com.google.android.gms.fonts"_s;
+
+ // First we access the content provider to get the signatures of the authority for the package
+ const auto context = QtAndroidPrivate::context();
+
+ auto packageManager = context.callMethod<PackageManager>("getPackageManager");
+ if (!packageManager.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to instantiate PackageManager");
+ return fontFamily;
+ }
+ const int signaturesField = PackageManager::getStaticField<int>("GET_SIGNATURES");
+ auto providerInfo = packageManager.callMethod<ProviderInfo>("resolveContentProvider",
+ authority, 0);
+ if (!providerInfo.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to resolve content provider");
+ return fontFamily;
+ }
+ const QString packageName = providerInfo.getField<QString>("packageName");
+ if (packageName != package) {
+ qCWarning(lcIconEngineFontDownload, "Mismatched provider package - expected '%s', got '%s'",
+ package.toUtf8().constData(), packageName.toUtf8().constData());
+ return fontFamily;
+ }
+ auto packageInfo = packageManager.callMethod<PackageInfo>("getPackageInfo",
+ package, signaturesField);
+ if (!packageInfo.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to get package info with signature field %d",
+ signaturesField);
+ return fontFamily;
+ }
+ const auto signatures = packageInfo.getField<Signature[]>("signatures");
+ if (!signatures.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to get signature array from package info");
+ return fontFamily;
+ }
+
+ // FontRequest wants a list of sets for the certificates
+ ArrayList outerList;
+ HashSet innerSet;
+ Q_ASSERT(outerList.isValid() && innerSet.isValid());
+
+ for (const auto &signature : signatures) {
+ const QJniArray<jbyte> byteArray = signature.callMethod<jbyte[]>("toByteArray");
+
+ // add takes an Object, not an Array
+ if (!innerSet.callMethod<jboolean>("add", byteArray.object<jobject>()))
+ qCWarning(lcIconEngineFontDownload, "Failed to add signature to set");
+ }
+ // Add the set to the list
+ if (!outerList.callMethod<jboolean>("add", innerSet.object()))
+ qCWarning(lcIconEngineFontDownload, "Failed to add set to certificate list");
+
+ // FontRequest constructor wants a List interface, not an ArrayList
+ FontRequest fontRequest(authority, package, query, outerList.object<List>());
+ if (!fontRequest.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to create font request for '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ // Call FontsContractCompat::fetchFonts with the FontRequest object
+ auto fontFamilyResult = FontsContractCompat::callStaticMethod<FontFamilyResult>(
+ "fetchFonts",
+ context,
+ CancellationSignal(nullptr),
+ fontRequest);
+ if (!fontFamilyResult.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to fetch fonts for query '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ enum class StatusCode {
+ OK = 0,
+ UNEXPECTED_DATA_PROVIDED = 1,
+ WRONG_CERTIFICATES = 2,
+ };
+
+ const StatusCode statusCode = fontFamilyResult.callMethod<StatusCode>("getStatusCode");
+ switch (statusCode) {
+ case StatusCode::OK:
+ break;
+ case StatusCode::UNEXPECTED_DATA_PROVIDED:
+ qCWarning(lcIconEngineFontDownload, "Provider returned unexpected data for query '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ case StatusCode::WRONG_CERTIFICATES:
+ qCWarning(lcIconEngineFontDownload, "Wrong Certificates provided in query '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ const auto fontInfos = fontFamilyResult.callMethod<FontInfo[]>("getFonts");
+ if (!fontInfos.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "FontFamilyResult::getFonts returned null object for '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ auto contentResolver = context.callMethod<ContentResolver>("getContentResolver");
+
+ for (QJniObject fontInfo : fontInfos) {
+ if (!fontInfo.isValid()) {
+ qCDebug(lcIconEngineFontDownload, "Received null-fontInfo object, skipping");
+ continue;
+ }
+ enum class ResultCode {
+ OK = 0,
+ FONT_NOT_FOUND = 1,
+ FONT_UNAVAILABLE = 2,
+ MALFORMED_QUERY = 3,
+ };
+ const ResultCode resultCode = fontInfo.callMethod<ResultCode>("getResultCode");
+ switch (resultCode) {
+ case ResultCode::OK:
+ break;
+ case ResultCode::FONT_NOT_FOUND:
+ qCWarning(lcIconEngineFontDownload, "Font '%s' could not be found",
+ query.toUtf8().constData());
+ return fontFamily;
+ case ResultCode::FONT_UNAVAILABLE:
+ qCWarning(lcIconEngineFontDownload, "Font '%s' is unavailable at",
+ query.toUtf8().constData());
+ return fontFamily;
+ case ResultCode::MALFORMED_QUERY:
+ qCWarning(lcIconEngineFontDownload, "Query string '%s' is malformed",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+ auto fontUri = fontInfo.callMethod<Uri>("getUri");
+ // in this case the Font URI is always a content scheme file, made
+ // so the app requesting it has permissions to open
+ auto fileDescriptor = contentResolver.callMethod<ParcelFileDescriptor>("openFileDescriptor",
+ fontUri, u"r"_s);
+ if (!fileDescriptor.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Font file '%s' not accessible",
+ fontUri.toString().toUtf8().constData());
+ continue;
+ }
+
+ int fd = fileDescriptor.callMethod<int>("detachFd");
+ QFile file;
+ file.open(fd, QFile::OpenModeFlag::ReadOnly, QFile::FileHandleFlag::AutoCloseHandle);
+ const QByteArray fontData = file.readAll();
+ qCDebug(lcIconEngineFontDownload) << "Font file read:" << fontData.size() << "bytes";
+ int fontId = QFontDatabase::addApplicationFontFromData(fontData);
+ loadedFamilies << QFontDatabase::applicationFontFamilies(fontId);
+ }
+ }
+
+ qCDebug(lcIconEngineFontDownload) << "Query '" << query << "' added families" << loadedFamilies;
+ if (!loadedFamilies.isEmpty())
+ fontFamily = loadedFamilies.first();
+ triedFonts[query] = fontFamily;
+ return fontFamily;
+}
+}
+
+QString QAndroidPlatformIconEngine::glyphs() const
+{
+ if (!QFontInfo(m_iconFont).exactMatch())
+ return {};
+
+ static constexpr std::pair<QLatin1StringView, QStringView> glyphMap[] = {
+ {"address-book-new"_L1, u"\ue0e0"},
+ {"application-exit"_L1, u"\ue5cd"},
+ {"appointment-new"_L1, u"\ue878"},
+ {"call-start"_L1, u"\ue0b0"},
+ {"call-stop"_L1, u"\ue0b1"},
+ {"contact-new"_L1, u"\uf22e"},
+ {"document-new"_L1, u"\ue89c"},
+ {"document-open"_L1, u"\ue2c8"},
+ {"document-open-recent"_L1, u"\ue4a7"},
+ {"document-page-setup"_L1, u"\uf88c"},
+ {"document-print"_L1, u"\ue8ad"},
+ {"document-print-preview"_L1, u"\uefb2"},
+ {"document-properties"_L1, u"\uf775"},
+ {"document-revert"_L1, u"\ue929"},
+ {"document-save"_L1, u"\ue161"},
+ {"document-save-as"_L1, u"\ueb60"},
+ {"document-send"_L1, u"\uf09b"},
+ {"edit-clear"_L1, u"\ue872"},
+ {"edit-copy"_L1, u"\ue14d"},
+ {"edit-cut"_L1, u"\ue14e"},
+ {"edit-delete"_L1, u"\ue14a"},
+ {"edit-find"_L1, u"\ue8b6"},
+ {"edit-find-replace"_L1, u"\ue881"},
+ {"edit-paste"_L1, u"\ue14f"},
+ {"edit-redo"_L1, u"\ue15a"},
+ {"edit-select-all"_L1, u"\ue162"},
+ {"edit-undo"_L1, u"\ue166"},
+ {"folder-new"_L1, u"\ue2cc"},
+ {"format-indent-less"_L1, u"\ue23d"},
+ {"format-indent-more"_L1, u"\ue23e"},
+ {"format-justify-center"_L1, u"\ue234"},
+ {"format-justify-fill"_L1, u"\ue235"},
+ {"format-justify-left"_L1, u"\ue236"},
+ {"format-justify-right"_L1, u"\ue237"},
+ {"format-text-direction-ltr"_L1, u"\ue247"},
+ {"format-text-direction-rtl"_L1, u"\ue248"},
+ {"format-text-bold"_L1, u"\ue238"},
+ {"format-text-italic"_L1, u"\ue23f"},
+ {"format-text-underline"_L1, u"\ue249"},
+ {"format-text-strikethrough"_L1, u"\ue246"},
+ {"go-bottom"_L1,u"\ue258"},
+ {"go-down"_L1,u"\uf1e3"},
+ {"go-first"_L1, u"\ue5dc"},
+ {"go-home"_L1, u"\ue88a"},
+ {"go-jump"_L1, u"\uf719"},
+ {"go-last"_L1, u"\ue5dd"},
+ {"go-next"_L1, u"\ue5c8"},
+ {"go-previous"_L1, u"\ue5c4"},
+ {"go-top"_L1, u"\ue25a"},
+ {"go-up"_L1, u"\uf1e0"},
+ {"help-about"_L1, u"\ue88e"},
+ {"help-contents"_L1, u"\ue8de"},
+ {"help-faq"_L1, u"\uf04c"},
+ {"insert-image"_L1, u"\ue43e"},
+ {"insert-link"_L1, u"\ue178"},
+ //{"insert-object"_L1, u"\u"},
+ {"insert-text"_L1, u"\uf827"},
+ {"list-add"_L1, u"\ue145"},
+ {"list-remove"_L1, u"\ue15b"},
+ {"mail-forward"_L1, u"\ue154"},
+ {"mail-mark-important"_L1, u"\ue937"},
+ //{"mail-mark-junk"_L1, u"\u"},
+ //{"mail-mark-notjunk"_L1, u"\u"},
+ {"mail-mark-read"_L1, u"\uf18c"},
+ {"mail-mark-unread"_L1, u"\ue9bc"},
+ {"mail-message-new"_L1, u"\ue3c9"},
+ {"mail-reply-all"_L1, u"\ue15f"},
+ {"mail-reply-sender"_L1, u"\ue15e"},
+ {"mail-send"_L1, u"\ue163"},
+ //{"mail-send-receive"_L1, u"\u"},
+ {"media-eject"_L1, u"\ue8fb"},
+ {"media-playback-pause"_L1, u"\ue034"},
+ {"media-playback-start"_L1, u"\ue037"},
+ {"media-playback-stop"_L1, u"\ue047"},
+ {"media-record"_L1, u"\uf679"},
+ {"media-seek-backward"_L1, u"\ue020"},
+ {"media-seek-forward"_L1, u"\ue01f"},
+ {"media-skip-backward"_L1, u"\ue045"},
+ {"media-skip-forward"_L1, u"\ue044"},
+ //{"object-flip-horizontal"_L1, u"\u"},
+ //{"object-flip-vertical"_L1, u"\u"},
+ {"object-rotate-left"_L1, u"\ue419"},
+ {"object-rotate-right"_L1, u"\ue41a"},
+ {"process-stop"_L1, u"\ue5c9"},
+ {"system-lock-screen"_L1, u"\ue897"},
+ {"system-log-out"_L1, u"\ue9ba"},
+ //{"system-run"_L1, u"\u"},
+ {"system-search"_L1, u"\uef70"},
+ {"system-reboot"_L1, u"\uf053"},
+ {"system-shutdown"_L1, u"\ue8ac"},
+ {"tools-check-spelling"_L1, u"\ue8ce"},
+ {"view-fullscreen"_L1, u"\ue5d0"},
+ {"view-refresh"_L1, u"\ue5d5"},
+ {"view-restore"_L1, u"\uf1cf"},
+ {"view-sort-ascending"_L1, u"\ue25a"},
+ {"view-sort-descending"_L1, u"\ue258"},
+ {"window-close"_L1, u"\ue5cd"},
+ {"window-new"_L1, u"\uf710"},
+ {"zoom-fit-best"_L1, u"\uea10"},
+ {"zoom-in"_L1, u"\ue8ff"},
+ {"zoom-original"_L1, u"\ue5d1"},
+ {"zoom-out"_L1, u"\ue900"},
+ {"process-working"_L1, u"\uef64"},
+ {"accessories-calculator"_L1, u"\uea5f"},
+ {"accessories-character-map"_L1, u"\uf8a3"},
+ {"accessories-dictionary"_L1, u"\uf539"},
+ {"accessories-text-editor"_L1, u"\ue262"},
+ {"help-browser"_L1, u"\ue887"},
+ {"multimedia-volume-control"_L1, u"\ue050"},
+ {"preferences-desktop-accessibility"_L1, u"\uf05d"},
+ {"preferences-desktop-font"_L1, u"\ue165"},
+ {"preferences-desktop-keyboard"_L1, u"\ue312"},
+ //{"preferences-desktop-locale"_L1, u"\u"},
+ {"preferences-desktop-multimedia"_L1, u"\uea75"},
+ //{"preferences-desktop-screensaver"_L1, u"\u"},
+ {"preferences-desktop-theme"_L1, u"\uf560"},
+ {"preferences-desktop-wallpaper"_L1, u"\ue1bc"},
+ {"system-file-manager"_L1, u"\ue2c7"},
+ {"system-software-install"_L1, u"\ueb71"},
+ {"system-software-update"_L1, u"\ue8d7"},
+ {"utilities-system-monitor"_L1, u"\uef5b"},
+ {"utilities-terminal"_L1, u"\ueb8e"},
+ //{"applications-accessories"_L1, u"\u"},
+ {"applications-development"_L1, u"\ue720"},
+ {"applications-engineering"_L1, u"\uea3d"},
+ {"applications-games"_L1, u"\uf135"},
+ //{"applications-graphics"_L1, u"\u"},
+ {"applications-internet"_L1, u"\ue80b"},
+ {"applications-multimedia"_L1, u"\uf06a"},
+ //{"applications-office"_L1, u"\u"},
+ //{"applications-other"_L1, u"\u"},
+ {"applications-science"_L1, u"\uea4b"},
+ //{"applications-system"_L1, u"\u"},
+ //{"applications-utilities"_L1, u"\u"},
+ {"preferences-desktop"_L1, u"\ueb97"},
+ //{"preferences-desktop-peripherals"_L1, u"\u"},
+ {"preferences-desktop-personal"_L1, u"\uf835"},
+ //{"preferences-other"_L1, u"\u"},
+ {"preferences-system"_L1, u"\ue8b8"},
+ {"preferences-system-network"_L1, u"\ue894"},
+ {"system-help"_L1, u"\ue887"},
+ //{"audio-card"_L1, u"\u"},
+ {"audio-input-microphone"_L1, u"\ue029"},
+ {"battery"_L1, u"\ue1a4"},
+ {"camera-photo"_L1, u"\ue412"},
+ {"camera-video"_L1, u"\ue04b"},
+ {"camera-web"_L1, u"\uf7a6"},
+ {"computer"_L1, u"\ue30a"},
+ {"drive-harddisk"_L1, u"\uf80e"},
+ {"drive-optical"_L1, u"\ue019"}, // same as media-optical
+ //{"drive-removable-media"_L1, u"\u"},
+ {"input-gaming"_L1, u"\uf5ee"},
+ {"input-keyboard"_L1, u"\ue312"},
+ {"input-mouse"_L1, u"\ue323"},
+ //{"input-tablet"_L1, u"\u"},
+ //{"media-flash"_L1, u"\u"},
+ //{"media-floppy"_L1, u"\u"},
+ {"media-optical"_L1, u"\ue019"},
+ //{"media-tape"_L1, u"\u"},
+ //{"modem"_L1, u"\u"},
+ //{"multimedia-player"_L1, u"\u"},
+ //{"network-wired"_L1, u"\u"},
+ {"network-wireless"_L1, u"\ue63e"},
+ //{"pda"_L1, u"\u"},
+ {"phone"_L1, u"\ue32c"},
+ {"printer"_L1, u"\ue8ad"},
+ {"scanner"_L1, u"\ue329"},
+ {"video-display"_L1, u"\uf06a"},
+ //{"emblem-default"_L1, u"\u"},
+ {"emblem-documents"_L1, u"\ue873"},
+ {"emblem-downloads"_L1, u"\uf090"},
+ {"emblem-favorite"_L1, u"\uf090"},
+ {"emblem-important"_L1, u"\ue645"},
+ {"emblem-mail"_L1, u"\ue158"},
+ {"emblem-photos"_L1, u"\ue413"},
+ //{"emblem-readonly"_L1, u"\u"},
+ {"emblem-shared"_L1, u"\ue413"},
+ //{"emblem-symbolic-link"_L1, u"\u"},
+ //{"emblem-synchronized"_L1, u"\u"},
+ {"emblem-system"_L1, u"\ue8b8"},
+ //{"emblem-unreadable"_L1, u"\u"},
+ {"folder"_L1, u"\ue2c7"},
+ //{"folder-remote"_L1, u"\u"},
+ {"network-server"_L1, u"\ue875"},
+ {"network-workgroup"_L1, u"\ue1a0"},
+ {"start-here"_L1, u"\ue089"},
+ {"user-bookmarks"_L1, u"\ue98b"},
+ {"user-desktop"_L1, u"\ue30a"},
+ {"user-home"_L1, u"\ue88a"},
+ {"user-trash"_L1, u"\ue872"},
+ {"appointment-missed"_L1, u"\ue615"},
+ {"appointment-soon"_L1, u"\uf540"},
+ {"audio-volume-high"_L1, u"\ue050"},
+ {"audio-volume-low"_L1, u"\ue04d"},
+ //{"audio-volume-medium"_L1, u"\u"},
+ {"audio-volume-muted"_L1, u"\ue04e"},
+ {"battery-caution"_L1, u"\ue19c"},
+ {"battery-low"_L1, u"\uf147"},
+ {"dialog-error"_L1, u"\ue000"},
+ {"dialog-information"_L1, u"\ue88e"},
+ {"dialog-password"_L1, u"\uf042"},
+ {"dialog-question"_L1, u"\ueb8b"},
+ {"dialog-warning"_L1, u"\ue002"},
+ {"folder-drag-accept"_L1, u"\ue9a3"},
+ {"folder-open"_L1, u"\ue2c8"},
+ {"folder-visiting"_L1, u"\ue8a7"},
+ {"image-loading"_L1, u"\ue41a"},
+ {"image-missing"_L1, u"\ue3ad"},
+ {"mail-attachment"_L1, u"\ue2bc"},
+ {"mail-unread"_L1, u"\uf18a"},
+ {"mail-read"_L1, u"\uf18c"},
+ //{"mail-replied"_L1, u"\u"},
+ //{"mail-signed"_L1, u"\u"},
+ //{"mail-signed-verified"_L1, u"\u"},
+ {"media-playlist-repeat"_L1, u"\ue040"},
+ {"media-playlist-shuffle"_L1, u"\ue043"},
+ {"network-error"_L1, u"\uead9"},
+ {"network-idle"_L1, u"\ue51f"},
+ {"network-offline"_L1, u"\uf239"},
+ {"network-receive"_L1, u"\ue2c0"},
+ {"network-transmit"_L1, u"\ue2c3"},
+ {"network-transmit-receive"_L1, u"\uea18"},
+ {"printer-error"_L1, u"\uf7a0"},
+ {"printer-printing"_L1, u"\uf7a1"},
+ {"security-high"_L1, u"\ue32a"},
+ {"security-medium"_L1, u"\ue9e0"},
+ {"security-low"_L1, u"\uf012"},
+ {"software-update-available"_L1, u"\ue923"},
+ {"software-update-urgent"_L1, u"\uf05a"},
+ {"sync-error"_L1, u"\ue629"},
+ {"sync-synchronizing"_L1, u"\ue627"},
+ //{"task-due"_L1, u"\u"},
+ //{"task-past-due"_L1, u"\u"},
+ {"user-available"_L1, u"\uf565"},
+ {"user-away"_L1, u"\ue510"},
+ //{"user-idle"_L1, u"\u"},
+ {"user-offline"_L1, u"\uf7b3"},
+ {"user-trash-full"_L1, u"\ue872"}, //delete
+ //{"user-trash-full"_L1, u"\ue92b"}, //delete_forever
+ {"weather-clear"_L1, u"\uf157"},
+ {"weather-clear-night"_L1, u"\uf159"},
+ {"weather-few-clouds"_L1, u"\uf172"},
+ {"weather-few-clouds-night"_L1, u"\uf174"},
+ {"weather-fog"_L1, u"\ue818"},
+ //{"weather-overcast"_L1, u"\u"},
+ {"weather-severe-alert"_L1, u"\ue002"}, //warning
+ //{"weather-severe-alert"_L1, u"\uebd3"},//severe_cold
+ {"weather-showers"_L1, u"\uf176"},
+ //{"weather-showers-scattered"_L1, u"\u"},
+ {"weather-snow"_L1, u"\ue80f"}, //snowing
+ //{"weather-snow"_L1, u"\ue2cd"}, //weather_snowy
+ //{"weather-snow"_L1, u"\ue810"},//cloudy_snowing
+ {"weather-storm"_L1, u"\uf070"},
+ };
+
+ const auto it = std::find_if(std::begin(glyphMap), std::end(glyphMap), [this](const auto &c){
+ return c.first == m_iconName;
+ });
+ return it != std::end(glyphMap) ? it->second.toString()
+ : (m_iconName.length() == 1 ? m_iconName : QString());
+}
+
+QAndroidPlatformIconEngine::QAndroidPlatformIconEngine(const QString &iconName)
+ : m_iconName(iconName)
+ , m_glyphs(glyphs())
+{
+ QString fontFamily;
+ // The MaterialIcons-*.ttf and MaterialSymbols* font files are available from
+ // https://github.com/google/material-design-icons/tree/master. If one of them is
+ // packaged as a resource with the application, then we use it. We prioritize
+ // a variable font.
+ const QStringList fontCandidates = {
+ "MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf",
+ "MaterialSymbolsRounded[FILL,GRAD,opsz,wght].ttf",
+ "MaterialSymbolsSharp[FILL,GRAD,opsz,wght].ttf",
+ "MaterialIcons-Regular.ttf",
+ "MaterialIconsOutlined-Regular.otf",
+ "MaterialIconsRound-Regular.otf",
+ "MaterialIconsSharp-Regular.otf",
+ "MaterialIconsTwoTone-Regular.otf",
+ };
+ for (const auto &fontCandidate : fontCandidates) {
+ fontFamily = FontProvider::fetchFont(u":/qt-project.org/icons/%1"_s.arg(fontCandidate));
+ if (!fontFamily.isEmpty())
+ break;
+ }
+
+ // Otherwise we try to download the Outlined version of Material Symbols
+ const QString key = qEnvironmentVariable("QT_GOOGLE_FONTS_KEY");
+ if (fontFamily.isEmpty() && !key.isEmpty())
+ fontFamily = FontProvider::fetchFont(u"key=%1&name=Material+Symbols+Outlined"_s.arg(key));
+
+ // last resort - use any Material Icons
+ if (fontFamily.isEmpty())
+ fontFamily = u"Material Icons"_s;
+ m_iconFont = QFont(fontFamily);
+}
+
+QAndroidPlatformIconEngine::~QAndroidPlatformIconEngine()
+{}
+
+QIconEngine *QAndroidPlatformIconEngine::clone() const
+{
+ return new QAndroidPlatformIconEngine(m_iconName);
+}
+
+QString QAndroidPlatformIconEngine::key() const
+{
+ return u"QAndroidPlatformIconEngine"_s;
+}
+
+QString QAndroidPlatformIconEngine::iconName()
+{
+ return m_iconName;
+}
+
+bool QAndroidPlatformIconEngine::isNull()
+{
+ if (m_glyphs.isEmpty())
+ return true;
+ const QChar c0 = m_glyphs.at(0);
+ const QFontMetrics fontMetrics(m_iconFont);
+ if (c0.category() == QChar::Other_Surrogate && m_glyphs.size() > 1)
+ return !fontMetrics.inFontUcs4(QChar::surrogateToUcs4(c0, m_glyphs.at(1)));
+ return !fontMetrics.inFont(c0);
+}
+
+QList<QSize> QAndroidPlatformIconEngine::availableSizes(QIcon::Mode, QIcon::State)
+{
+ return {{16, 16}, {24, 24}, {48, 48}, {128, 128}};
+}
+
+QSize QAndroidPlatformIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return QIconEngine::actualSize(size, mode, state);
+}
+
+QPixmap QAndroidPlatformIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return scaledPixmap(size, mode, state, 1.0);
+}
+
+QPixmap QAndroidPlatformIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ const quint64 cacheKey = calculateCacheKey(mode, state);
+ if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
+ m_pixmap = QPixmap(size * scale);
+ m_pixmap.fill(Qt::transparent);
+ m_pixmap.setDevicePixelRatio(scale);
+
+ QPainter painter(&m_pixmap);
+ paint(&painter, QRect(QPoint(), size), mode, state);
+
+ m_cacheKey = cacheKey;
+ }
+
+ return m_pixmap;
+}
+
+void QAndroidPlatformIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ Q_UNUSED(state);
+
+ painter->save();
+ QFont renderFont(m_iconFont);
+ renderFont.setPixelSize(rect.height());
+ painter->setFont(renderFont);
+
+ QPalette palette;
+ switch (mode) {
+ case QIcon::Active:
+ painter->setPen(palette.color(QPalette::Active, QPalette::Text));
+ break;
+ case QIcon::Normal:
+ painter->setPen(palette.color(QPalette::Active, QPalette::Text));
+ break;
+ case QIcon::Disabled:
+ painter->setPen(palette.color(QPalette::Disabled, QPalette::Text));
+ break;
+ case QIcon::Selected:
+ painter->setPen(palette.color(QPalette::Active, QPalette::HighlightedText));
+ break;
+ }
+
+ painter->drawText(rect, Qt::AlignCenter, m_glyphs);
+ painter->restore();
+}
+
+QT_END_NAMESPACE