diff --git a/Telegram/SourceFiles/data/data_types.cpp b/Telegram/SourceFiles/data/data_types.cpp index 204d99cfb..064505355 100644 --- a/Telegram/SourceFiles/data/data_types.cpp +++ b/Telegram/SourceFiles/data/data_types.cpp @@ -23,6 +23,8 @@ constexpr auto kWebDocumentCacheTag = 0x0000020000000000ULL; constexpr auto kWebDocumentCacheMask = 0x000000FFFFFFFFFFULL; constexpr auto kUrlCacheTag = 0x0000030000000000ULL; constexpr auto kUrlCacheMask = 0x000000FFFFFFFFFFULL; +constexpr auto kGeoPointCacheTag = 0x0000040000000000ULL; +constexpr auto kGeoPointCacheMask = 0x000000FFFFFFFFFFULL; } // namespace @@ -74,6 +76,18 @@ Storage::Cache::Key UrlCacheKey(const QString &location) { }; } +Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location) { + const auto zoomscale = ((uint32(location.zoom) & 0x0FU) << 8) + | (uint32(location.scale) & 0x0FU); + const auto widthheight = ((uint32(location.width) & 0xFFFFU) << 16) + | (uint32(location.height) & 0xFFFFU); + return Storage::Cache::Key{ + Data::kGeoPointCacheTag | (uint64(zoomscale) << 32) | widthheight, + (uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32) + | uint64(std::round(std::abs(location.lon + 360.) * 1000000)) + }; +} + } // namespace Data void AudioMsgId::setTypeFromAudio() { diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 8aabd7499..9766fb824 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -25,6 +25,7 @@ class InputField; class StorageImageLocation; class WebFileLocation; +struct GeoPointLocation; namespace Data { @@ -40,6 +41,7 @@ Storage::Cache::Key DocumentCacheKey(int32 dcId, uint64 id); Storage::Cache::Key StorageCacheKey(const StorageImageLocation &location); Storage::Cache::Key WebDocumentCacheKey(const WebFileLocation &location); Storage::Cache::Key UrlCacheKey(const QString &location); +Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location); constexpr auto kImageCacheTag = uint8(0x01); constexpr auto kStickerCacheTag = uint8(0x02); diff --git a/Telegram/SourceFiles/history/history_location_manager.cpp b/Telegram/SourceFiles/history/history_location_manager.cpp index 66655c6b3..a8de24cd5 100644 --- a/Telegram/SourceFiles/history/history_location_manager.cpp +++ b/Telegram/SourceFiles/history/history_location_manager.cpp @@ -16,6 +16,28 @@ namespace { constexpr auto kCoordPrecision = 8; constexpr auto kMaxHttpRedirects = 5; +GeoPointLocation ComputeLocation(const LocationCoords &coords) { + int32 w = st::locationSize.width(), h = st::locationSize.height(); + int32 zoom = 15, scale = 1; + if (cScale() == dbisTwo || cRetina()) { + scale = 2; + zoom = 16; + } else { + w = convertScale(w); + h = convertScale(h); + } + + auto result = GeoPointLocation(); + result.lat = coords.lat(); + result.lon = coords.lon(); + result.access = coords.accessHash(); + result.width = w; + result.height = h; + result.zoom = zoom; + result.scale = scale; + return result; +} + } // namespace QString LocationClickHandler::copyToClipboardText() const { @@ -37,200 +59,11 @@ void LocationClickHandler::setup() { _text = qsl("https://maps.google.com/maps?q=") + latlon + qsl("&ll=") + latlon + qsl("&z=16"); } -namespace { -LocationManager *locationManager = nullptr; -} // namespace - -void initLocationManager() { - if (!locationManager) { - locationManager = new LocationManager(); - locationManager->init(); - } -} - -void deinitLocationManager() { - if (locationManager) { - locationManager->deinit(); - delete locationManager; - locationManager = nullptr; - } -} - -void LocationManager::init() { - if (manager) delete manager; - manager = new QNetworkAccessManager(); - - connect(manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(onFailed(QNetworkReply*))); -#ifndef OS_MAC_OLD - connect(manager, SIGNAL(sslErrors(QNetworkReply*, const QList&)), this, SLOT(onFailed(QNetworkReply*))); -#endif // OS_MAC_OLD - connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*))); - - if (notLoadedPlaceholder) { - delete notLoadedPlaceholder->v(); - delete notLoadedPlaceholder; - } - auto data = QImage(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - data.fill(st::imageBgTransparent->c); - data.setDevicePixelRatio(cRetinaFactor()); - notLoadedPlaceholder = new ImagePtr(App::pixmapFromImageInPlace(std::move(data)), "GIF"); -} - -void LocationManager::deinit() { - if (manager) { - delete manager; - manager = nullptr; - } - if (notLoadedPlaceholder) { - delete notLoadedPlaceholder->v(); - delete notLoadedPlaceholder; - notLoadedPlaceholder = nullptr; - } - dataLoadings.clear(); - imageLoadings.clear(); -} - -void LocationManager::getData(LocationData *data) { - if (!manager) { - DEBUG_LOG(("App Error: getting image link data without manager init!")); - return failed(data); - } - - int32 w = st::locationSize.width(), h = st::locationSize.height(); - int32 zoom = 13, scale = 1; - if (cScale() == dbisTwo || cRetina()) { - scale = 2; - } else { - w = convertScale(w); - h = convertScale(h); - } - auto coords = data->coords.latAsString() + ',' + data->coords.lonAsString(); - QString url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + coords + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + coords + qsl("&sensor=false"); - QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); - imageLoadings[reply] = data; -} - -void LocationManager::onFinished(QNetworkReply *reply) { - if (!manager) return; - if (reply->error() != QNetworkReply::NoError) return onFailed(reply); - - QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - if (statusCode.isValid()) { - int status = statusCode.toInt(); - if (status == 301 || status == 302) { - QString loc = reply->header(QNetworkRequest::LocationHeader).toString(); - if (!loc.isEmpty()) { - QMap::iterator i = dataLoadings.find(reply); - if (i != dataLoadings.cend()) { - LocationData *d = i.value(); - if (serverRedirects.constFind(d) == serverRedirects.cend()) { - serverRedirects.insert(d, 1); - } else if (++serverRedirects[d] > kMaxHttpRedirects) { - DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc)); - return onFailed(reply); - } - dataLoadings.erase(i); - dataLoadings.insert(manager->get(QNetworkRequest(loc)), d); - return; - } else if ((i = imageLoadings.find(reply)) != imageLoadings.cend()) { - LocationData *d = i.value(); - if (serverRedirects.constFind(d) == serverRedirects.cend()) { - serverRedirects.insert(d, 1); - } else if (++serverRedirects[d] > kMaxHttpRedirects) { - DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc)); - return onFailed(reply); - } - imageLoadings.erase(i); - imageLoadings.insert(manager->get(QNetworkRequest(loc)), d); - return; - } - } - } - if (status != 200) { - DEBUG_LOG(("Network Error: Bad HTTP status received in onFinished() for image link: %1").arg(status)); - return onFailed(reply); - } - } - - LocationData *d = 0; - QMap::iterator i = dataLoadings.find(reply); - if (i != dataLoadings.cend()) { - d = i.value(); - dataLoadings.erase(i); - - QJsonParseError e; - QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &e); - if (e.error != QJsonParseError::NoError) { - DEBUG_LOG(("JSON Error: Bad json received in onFinished() for image link")); - return onFailed(reply); - } - failed(d); - - if (App::main()) App::main()->update(); - } else { - i = imageLoadings.find(reply); - if (i != imageLoadings.cend()) { - d = i.value(); - imageLoadings.erase(i); - - QPixmap thumb; - QByteArray format; - QByteArray data(reply->readAll()); - { - QBuffer buffer(&data); - QImageReader reader(&buffer); -#ifndef OS_MAC_OLD - reader.setAutoTransform(true); -#endif // OS_MAC_OLD - thumb = QPixmap::fromImageReader(&reader, Qt::ColorOnly); - format = reader.format(); - thumb.setDevicePixelRatio(cRetinaFactor()); - if (format.isEmpty()) format = QByteArray("JPG"); - } - d->loading = false; - d->thumb = thumb.isNull() ? (*notLoadedPlaceholder) : ImagePtr(thumb, format); - serverRedirects.remove(d); - if (App::main()) App::main()->update(); - } - } -} - -void LocationManager::onFailed(QNetworkReply *reply) { - if (!manager) return; - - LocationData *d = 0; - QMap::iterator i = dataLoadings.find(reply); - if (i != dataLoadings.cend()) { - d = i.value(); - dataLoadings.erase(i); - } else { - i = imageLoadings.find(reply); - if (i != imageLoadings.cend()) { - d = i.value(); - imageLoadings.erase(i); - } - } - DEBUG_LOG(("Network Error: failed to get data for image link %1,%2 error %3").arg(d ? d->coords.latAsString() : QString()).arg(d ? d->coords.lonAsString() : QString()).arg(reply->errorString())); - if (d) { - failed(d); - } -} - -void LocationManager::failed(LocationData *data) { - data->loading = false; - data->thumb = *notLoadedPlaceholder; - serverRedirects.remove(data); +LocationData::LocationData(const LocationCoords &coords) +: coords(coords) +, thumb(ComputeLocation(coords)) { } void LocationData::load(Data::FileOrigin origin) { - if (!thumb->isNull()) { - return thumb->load(origin, false, false); - } else if (loading) { - return; - } - - loading = true; - if (locationManager) { - locationManager->getData(this); - } + thumb->load(origin, false, false); } diff --git a/Telegram/SourceFiles/history/history_location_manager.h b/Telegram/SourceFiles/history/history_location_manager.h index ba4ff233c..2b0aa15f9 100644 --- a/Telegram/SourceFiles/history/history_location_manager.h +++ b/Telegram/SourceFiles/history/history_location_manager.h @@ -7,9 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -void initLocationManager(); -void deinitLocationManager(); - class LocationCoords { public: LocationCoords() = default; @@ -32,9 +29,19 @@ public: MTP_long(_access)); } + float64 lat() const { + return _lat; + } + float64 lon() const { + return _lon; + } + uint64 accessHash() const { + return _access; + } + private: static QString asString(float64 value) { - static constexpr auto kPrecision = 6; + constexpr auto kPrecision = 6; return QString::number(value, 'f', kPrecision); } @@ -63,14 +70,13 @@ private: }; struct LocationData { - LocationData(const LocationCoords &coords) : coords(coords), loading(false) { - } + LocationData(const LocationCoords &coords); LocationCoords coords; ImagePtr thumb; - bool loading; void load(Data::FileOrigin origin); + }; class LocationClickHandler : public ClickHandler { @@ -99,31 +105,3 @@ private: QString _text; }; - -class LocationManager : public QObject { - Q_OBJECT - -public: - void init(); - void reinit(); - void deinit(); - - void getData(LocationData *data); - - ~LocationManager() { - deinit(); - } - -public slots: - void onFinished(QNetworkReply *reply); - void onFailed(QNetworkReply *reply); - -private: - void failed(LocationData *data); - - QNetworkAccessManager *manager = nullptr; - QMap dataLoadings, imageLoadings; - QMap serverRedirects; - ImagePtr *notLoadedPlaceholder = nullptr; - -}; diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 37076742e..1c01f2bd0 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -147,7 +147,6 @@ Messenger::Messenger(not_null launcher) Shortcuts::start(); - initLocationManager(); App::initMedia(); Local::ReadMapState state = Local::readMap(QByteArray()); @@ -1030,7 +1029,6 @@ Messenger::~Messenger() { stopWebLoadManager(); App::deinitMedia(); - deinitLocationManager(); Window::Theme::Unload(); diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index ac6dc4548..8203ed2da 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -559,6 +559,30 @@ mtpFileLoader::mtpFileLoader( _queue = &i.value(); } +mtpFileLoader::mtpFileLoader( + const GeoPointLocation *location, + int32 size, + LoadFromCloudSetting fromCloud, + bool autoLoading, + uint8 cacheTag) +: FileLoader( + QString(), + size, + UnknownFileLocation, + LoadToCacheAsWell, + fromCloud, + autoLoading, + cacheTag) +, _dcId(Global::WebFileDcId()) +, _geoLocation(location) { + auto shiftedDcId = MTP::downloadDcId(_dcId, 0); + auto i = queues.find(shiftedDcId); + if (i == queues.cend()) { + i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries)); + } + _queue = &i.value(); +} + int32 mtpFileLoader::currentOffset(bool includeSkipped) const { return (_fileIsOpen ? _file.size() : _data.size()) - (includeSkipped ? 0 : _skippedBytes); } @@ -663,6 +687,25 @@ void mtpFileLoader::makeRequest(int offset) { rpcFail(&mtpFileLoader::partFailed), shiftedDcId, 50); + } else if (_geoLocation) { + Assert(requestData.dcId == _dcId); + return MTP::send( + MTPupload_GetWebFile( + MTP_inputWebFileGeoPointLocation( + MTP_inputGeoPoint( + MTP_double(_geoLocation->lat), + MTP_double(_geoLocation->lon)), + MTP_long(_geoLocation->access), + MTP_int(_geoLocation->width), + MTP_int(_geoLocation->height), + MTP_int(_geoLocation->zoom), + MTP_int(_geoLocation->scale)), + MTP_int(offset), + MTP_int(limit)), + rpcDone(&mtpFileLoader::webPartLoaded), + rpcFail(&mtpFileLoader::partFailed), + shiftedDcId, + 50); } else { Assert(requestData.dcId == _dcId); return MTP::send( @@ -1105,9 +1148,11 @@ void mtpFileLoader::changeCDNParams( std::optional mtpFileLoader::cacheKey() const { if (_urlLocation) { return Data::WebDocumentCacheKey(*_urlLocation); + } else if (_geoLocation) { + return Data::GeoPointCacheKey(*_geoLocation); } else if (_location) { return Data::StorageCacheKey(*_location); - } else if (_toCache == LoadToCacheAsWell) { + } else if (_toCache == LoadToCacheAsWell && _id != 0) { return Data::DocumentCacheKey(_dcId, _id); } return std::nullopt; diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index 3fe7daadf..98756e9db 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -227,6 +227,12 @@ public: LoadFromCloudSetting fromCloud, bool autoLoading, uint8 cacheTag); + mtpFileLoader( + const GeoPointLocation *location, + int32 size, + LoadFromCloudSetting fromCloud, + bool autoLoading, + uint8 cacheTag); int32 currentOffset(bool includeSkipped = false) const override; Data::FileOrigin fileOrigin() const override; @@ -306,6 +312,7 @@ private: QByteArray _fileReference; const WebFileLocation *_urlLocation = nullptr; // for webdocument locations + const GeoPointLocation *_geoLocation = nullptr; // for webdocument locations Data::FileOrigin _origin; diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index 58b5c6733..c35b32d20 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -392,6 +392,9 @@ StorageImages storageImages; using WebFileImages = QMap; WebFileImages webFileImages; +using GeoPointImages = QMap; +GeoPointImages geoPointImages; + int64 globalAcquiredSize = 0; uint64 PixKey(int width, int height, Images::Options options) { @@ -947,6 +950,9 @@ void clearStorageImages() { for (auto image : base::take(webFileImages)) { delete image; } + for (auto image : base::take(geoPointImages)) { + delete image; + } } void clearAllImages() { @@ -1229,6 +1235,40 @@ FileLoader *WebFileImage::createLoader( Data::kImageCacheTag); } +GeoPointImage::GeoPointImage(const GeoPointLocation &location) +: _location(location) { +} + +std::optional GeoPointImage::cacheKey() const { + return Data::GeoPointCacheKey(_location); +} + +int GeoPointImage::countWidth() const { + return _location.width; +} + +int GeoPointImage::countHeight() const { + return _location.height; +} + +void GeoPointImage::setInformation(int size, int width, int height) { + _size = size; + _location.width = width; + _location.height = height; +} + +FileLoader *GeoPointImage::createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + return new mtpFileLoader( + &_location, + _size, + fromCloud, + autoLoading, + Data::kImageCacheTag); +} + DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation()) , _loadRequested(false) , _loadCancelled(false) @@ -1571,6 +1611,17 @@ WebFileImage *getImage( return i.value(); } +GeoPointImage *getImage(const GeoPointLocation &location) { + auto key = storageKey(location); + auto i = geoPointImages.constFind(key); + if (i == geoPointImages.cend()) { + i = geoPointImages.insert( + key, + new GeoPointImage(location)); + } + return i.value(); +} + } // namespace internal ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index 7b1205968..6edfec9a0 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -251,6 +251,34 @@ inline bool operator!=(const WebFileLocation &a, const WebFileLocation &b) { return !(a == b); } +struct GeoPointLocation { + float64 lat = 0.; + float64 lon = 0.; + uint64 access = 0; + int32 width = 0; + int32 height = 0; + int32 zoom = 0; + int32 scale = 0; +}; + +inline bool operator==( + const GeoPointLocation &a, + const GeoPointLocation &b) { + return (a.lat == b.lat) + && (a.lon == b.lon) + && (a.access == b.access) + && (a.width == b.width) + && (a.height == b.height) + && (a.zoom == b.zoom) + && (a.scale == b.scale); +} + +inline bool operator!=( + const GeoPointLocation &a, + const GeoPointLocation &b) { + return !(a == b); +} + class DelayedStorageImage; class HistoryItem; @@ -450,6 +478,12 @@ inline StorageKey storageKey(const WebFileLocation &location) { *reinterpret_cast(sha.data()), *reinterpret_cast(sha.data() + sizeof(uint64))); } +inline StorageKey storageKey(const GeoPointLocation &location) { + return StorageKey( + (uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32) + | uint64(std::round(std::abs(location.lon + 360.) * 1000000)), + (uint64(location.width) << 32) | uint64(location.height)); +} class RemoteImage : public Image { public: @@ -569,6 +603,27 @@ protected: }; +class GeoPointImage : public RemoteImage { +public: + GeoPointImage(const GeoPointLocation &location); + + std::optional cacheKey() const override; + +protected: + void setInformation(int size, int width, int height) override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + int countWidth() const override; + int countHeight() const override; + + GeoPointLocation _location; + int _size = 0; + +}; + class DelayedStorageImage : public StorageImage { public: DelayedStorageImage(); @@ -668,6 +723,8 @@ WebFileImage *getImage( const WebFileLocation &location, QSize box, int size = 0); +GeoPointImage *getImage( + const GeoPointLocation &location); } // namespace internal @@ -700,6 +757,9 @@ public: ImagePtr(const WebFileLocation &location, QSize box, int size = 0) : Parent(internal::getImage(location, box, size)) { } + ImagePtr(const GeoPointLocation &location) + : Parent(internal::getImage(location)) { + } ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); ImagePtr(int32 width, int32 height) : Parent(internal::getImage(width, height)) { }