* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define MLOG_TAG "AccurateRefresh::AnalysisAlbumRefreshExecution"
#include "analysis_album_refresh_execution.h"
#include <sstream>
#include "accurate_common_data.h"
#include "album_asset_helper.h"
#include "medialibrary_rdb_utils.h"
#include "medialibrary_tracer.h"
#include "medialibrary_unistore_manager.h"
#include "media_file_utils.h"
#include "media_log.h"
using namespace std;
using namespace OHOS::NativeRdb;
namespace OHOS {
namespace Media {
namespace AccurateRefresh {
mutex AnalysisAlbumRefreshExecution::albumRefreshMtx_;
int32_t AnalysisAlbumRefreshExecution::RefreshAlbum(const vector<PhotoAssetChangeData> &assetChangeDatas,
NotifyAlbumType notifyAlbumType, bool isRefreshWithDateModified)
{
MediaLibraryTracer tracer;
tracer.Start("AnalysisAlbumRefreshExecution::RefreshAlbum");
lock_guard<mutex> lock(albumRefreshMtx_);
isRefreshWithDateModified_ = isRefreshWithDateModified;
ResetExecutionStatus();
int32_t ret = CalAnalysisRefreshInfos(assetChangeDatas);
CHECK_AND_RETURN_RET_LOG(ret == ACCURATE_REFRESH_RET_OK, ret,
"CalAnalysisRefreshInfos failed");
CHECK_AND_RETURN_RET_INFO_LOG(!albumCtxMap_.empty(),
ACCURATE_REFRESH_RET_OK, "RefreshAnalysisAlbum: no album needs refresh");
return CommitAnalysisAlbumRefreshResults(notifyAlbumType);
}
int32_t AnalysisAlbumRefreshExecution::CalAnalysisRefreshInfos(const vector<PhotoAssetChangeData> &assetChangeDatas)
{
CHECK_AND_RETURN_RET_INFO_LOG(!assetChangeDatas.empty(),
ACCURATE_REFRESH_RET_OK, "No asset changes. Skip analysis refresh.");
AlbumIdSet affectedAlbumIds;
PrepareAffectedAssets(assetChangeDatas, affectedAlbumIds);
CHECK_AND_RETURN_RET_INFO_LOG(!affectedAlbumIds.empty(),
ACCURATE_REFRESH_RET_OK, "No affected analysis albums found.");
BuildContexts(affectedAlbumIds);
CalculateAlbumChanges(assetChangeDatas);
ProcessCoverChanges();
MEDIA_DEBUG_LOG("CalAnalysisRefreshInfos finish");
return ACCURATE_REFRESH_RET_OK;
}
void AnalysisAlbumRefreshExecution::HandleInfoRelatedShootingModeTypes(const PhotoAssetChangeInfo &info,
AlbumIdSet &affectedAlbumIds)
{
CHECK_AND_RETURN(info.fileId_ != INVALID_INT32_VALUE);
vector<ShootingModeAlbumType> albumTypes = ShootingModeAlbum::GetShootingModeAlbumOfAsset(
info.subType_, info.mimeType_, info.movingPhotoEffectMode_, info.frontCamera_, info.shootingMode_);
for (const auto &type : albumTypes) {
int32_t albumId;
auto it = shootingModeAlbumIdMap_.find(type);
if (it == shootingModeAlbumIdMap_.end()) {
CHECK_AND_CONTINUE_ERR_LOG(MediaLibraryRdbUtils::QueryShootingModeAlbumIdByType(type, albumId),
"Query corresponding shooting mode album id failed, type: %{public}d", static_cast<int32_t>(type));
shootingModeAlbumIdMap_.emplace(type, albumId);
} else {
albumId = it->second;
}
auto &albumSet = assetAlbumMap_.try_emplace(info.fileId_, std::unordered_set<int32_t>{}).first->second;
albumSet.insert(albumId);
affectedAlbumIds.insert(albumId);
}
}
void AnalysisAlbumRefreshExecution::PrepareAffectedAssets(const vector<PhotoAssetChangeData> &assetChangeDatas,
AlbumIdSet &affectedAlbumIds)
{
AlbumIdSet fileIdSet;
for (const auto &assetChangeData : assetChangeDatas) {
int32_t fileId = assetChangeData.GetFileId();
CHECK_AND_CONTINUE(fileId != INVALID_INT32_VALUE);
fileIdSet.insert(fileId);
HandleInfoRelatedShootingModeTypes(assetChangeData.infoBeforeChange_, affectedAlbumIds);
HandleInfoRelatedShootingModeTypes(assetChangeData.infoAfterChange_, affectedAlbumIds);
}
vector<string> outFileIdStrs;
outFileIdStrs.reserve(fileIdSet.size());
for (int32_t fileId : fileIdSet) {
outFileIdStrs.emplace_back(to_string(fileId));
}
CHECK_AND_PRINT_LOG(MediaLibraryRdbUtils::QueryAnalysisAlbumMapByAssets(outFileIdStrs,
assetAlbumMap_, affectedAlbumIds) == E_OK, "QueryAnalysisAlbumMapByAssets failed");
}
void AnalysisAlbumRefreshExecution::BuildContexts(const AlbumIdSet &affectedAlbumIds)
{
vector<UpdateAlbumData> albumDatas;
unordered_map<string, vector<int32_t>> portraitGroupMap;
std::vector<int32_t> vecAlbumIds(affectedAlbumIds.begin(), affectedAlbumIds.end());
CHECK_AND_RETURN_LOG(
MediaLibraryRdbUtils::QueryAnalysisAlbumsForAccurateRefresh(vecAlbumIds, albumDatas, portraitGroupMap) == E_OK,
"QueryAnalysisAlbumsForAccurateRefresh failed");
for (auto &data : albumDatas) {
AlbumContext ctx;
ctx.baseInfo = data;
ctx.analyzer = AnalysisStrategyRegistry::GetAnalyzer(data.albumSubtype);
albumCtxMap_.emplace(data.albumId, move(ctx));
}
for (auto &kv : portraitGroupMap) {
const string &groupTag = kv.first;
const vector<int32_t> &albumIds = kv.second;
CHECK_AND_CONTINUE(!albumIds.empty());
GroupContext gctx;
gctx.groupTag = groupTag;
gctx.albumIds = albumIds;
if (auto it = albumCtxMap_.find(albumIds.front()); it != albumCtxMap_.end()) {
gctx.baseInfo = it->second.baseInfo;
gctx.analyzer = it->second.analyzer;
}
groupCtxMap_.emplace(groupTag, move(gctx));
}
}
void AnalysisAlbumRefreshExecution::CalculateAlbumChanges(const vector<PhotoAssetChangeData> &assetChangeDatas)
{
for (const auto &data : assetChangeDatas) {
int32_t fileId = data.GetFileId();
auto it = assetAlbumMap_.find(fileId);
CHECK_AND_CONTINUE(it != assetAlbumMap_.end());
const AlbumIdSet &albumIds = it->second;
unordered_set<string> visitedGroups;
for (const auto &albumId : albumIds) {
auto aIt = albumCtxMap_.find(albumId);
CHECK_AND_CONTINUE_ERR_LOG(aIt != albumCtxMap_.end(), "No recorded album, id: %{public}d", albumId);
AlbumContext &actx = aIt->second;
auto subtype = static_cast<PhotoAlbumSubType>(actx.baseInfo.albumSubtype);
if (subtype == PhotoAlbumSubType::PORTRAIT) {
const string &groupTag = actx.baseInfo.groupTag;
CHECK_AND_CONTINUE(!groupTag.empty() && visitedGroups.insert(groupTag).second);
auto gIt = groupCtxMap_.find(groupTag);
CHECK_AND_CONTINUE(gIt != groupCtxMap_.end() && gIt->second.analyzer != nullptr);
int32_t delta = gIt->second.analyzer->CalcDataChange(data,
gIt->second.baseInfo, gIt->second.refreshInfo);
InsertRefreshMapByDelta(fileId, gIt->second.albumIds, delta);
continue;
}
int32_t delta = actx.analyzer->CalcDataChange(data, actx.baseInfo, actx.refreshInfo);
InsertRefreshMapByDelta(fileId, {albumId}, delta);
}
}
}
void AnalysisAlbumRefreshExecution::InsertRefreshMapByDelta(int32_t fileId,
const vector<int32_t> &albumIds, int32_t delta)
{
CHECK_AND_RETURN(fileId != INVALID_INT32_VALUE);
auto it = assetAlbumRefreshMap_.find(fileId);
if (it == assetAlbumRefreshMap_.end()) {
it = assetAlbumRefreshMap_.emplace(fileId,
std::make_pair(std::unordered_set<int32_t>{}, std::unordered_set<int32_t>{})).first;
}
auto &beforeSet = it->second.first;
auto &afterSet = it->second.second;
for (auto albumId : albumIds) {
if (delta <= 0) {
beforeSet.insert(albumId);
}
if (delta >= 0) {
afterSet.insert(albumId);
}
}
}
void AnalysisAlbumRefreshExecution::ProcessCoverChanges()
{
for (auto &[gt, gctx] : groupCtxMap_) {
CHECK_AND_CONTINUE_INFO_LOG(gctx.refreshInfo.HasValidRefreshInfo(),
"No need to process cover tag: %{public}s", gctx.groupTag.c_str());
CHECK_AND_CONTINUE_ERR_LOG(gctx.analyzer != nullptr, "Analyzer is invalid");
gctx.analyzer->ApplyCoverChange(gctx.baseInfo, gctx.refreshInfo);
for (int32_t albumId : gctx.albumIds) {
if (auto it = albumCtxMap_.find(albumId); it != albumCtxMap_.end()) {
it->second.refreshInfo.MergeFromGroup(gctx.refreshInfo);
}
}
}
for (auto &[albumId, actx] : albumCtxMap_) {
CHECK_AND_CONTINUE_DEBUG_LOG(actx.refreshInfo.HasValidRefreshInfo(),
"No need to process cover albumId: %{public}d", actx.baseInfo.albumId);
bool isPortrait =
actx.baseInfo.albumSubtype == static_cast<int32_t>(PhotoAlbumSubType::PORTRAIT);
if (!isPortrait && actx.analyzer) {
actx.analyzer->ApplyCoverChange(actx.baseInfo, actx.refreshInfo);
}
}
}
int32_t AnalysisAlbumRefreshExecution::CommitAnalysisAlbumRefreshResults(NotifyAlbumType notifyAlbumType)
{
std::vector<BatchUpdateItem> batchItems;
for (auto &[albumId, actx] : albumCtxMap_) {
CHECK_AND_CONTINUE_INFO_LOG(actx.refreshInfo.deltaCount_ != 0 || actx.refreshInfo.needRefreshCover_,
"No need to commit refresh, albumId: %{public}d", actx.baseInfo.albumId);
string newCover = "";
int32_t newCount = INVALID_INT32_VALUE;
ConcludeAlbumRefreshValues(actx.baseInfo, actx.refreshInfo, newCover, newCount);
bool needUpdateCount = newCount != actx.baseInfo.albumCount;
bool needUpdateCover = actx.refreshInfo.needRefreshCover_ &&
!(newCover == actx.baseInfo.albumCoverUri && newCount > 0);
UpdateNotifyContext(actx.baseInfo.albumId, needUpdateCount || needUpdateCover);
CHECK_AND_CONTINUE_INFO_LOG(needUpdateCount || needUpdateCover,
"No field has changed, albumId: %{public}d", actx.baseInfo.albumId);
MEDIA_INFO_LOG("Commit result, albumId: %{public}d, subType: %{public}d, needRefreshCover: %{public}d, "
"count: %{public}d -> %{public}d, cover: {%{public}s} -> {%{public}s}, refreshCover: %{public}s",
actx.baseInfo.albumId, actx.baseInfo.albumSubtype, actx.refreshInfo.needRefreshCover_,
actx.baseInfo.albumCount, newCount, MediaFileUtils::DesensitizeUri(actx.baseInfo.albumCoverUri).c_str(),
MediaFileUtils::DesensitizeUri(newCover).c_str(), actx.refreshInfo.refreshCover_.c_str());
batchItems.push_back({
albumId,
needUpdateCount,
needUpdateCover,
newCount,
newCover,
actx.baseInfo.albumSubtype
});
CheckBatchCountForUpdateAndNotify(batchItems);
}
if (!batchItems.empty()) {
std::string sql;
vector<NativeRdb::ValueObject> bindArgs;
if (AnalysisAlbumBatchUpdateHelper::BuildCaseSql(batchItems, sql, bindArgs)) {
albumRefresh_.ExecuteSql(sql, bindArgs, RdbOperation::RDB_OPERATION_UPDATE);
}
}
MEDIA_INFO_LOG("CommitAnalysisAlbumRefreshResults done. affected: %{public}zu", batchItems.size());
return ACCURATE_REFRESH_RET_OK;
}
void AnalysisAlbumRefreshExecution::CheckBatchCountForUpdateAndNotify(std::vector<BatchUpdateItem> &batchItems)
{
CHECK_AND_RETURN(batchItems.size() >= MAX_AFFECTED_ALBUM_LENGTH);
std::string sql;
vector<NativeRdb::ValueObject> bindArgs;
if (AnalysisAlbumBatchUpdateHelper::BuildCaseSql(batchItems, sql, bindArgs)) {
albumRefresh_.ExecuteSql(sql, bindArgs, RdbOperation::RDB_OPERATION_UPDATE);
}
vector<int32_t> albumIds;
albumIds.reserve(MAX_AFFECTED_ALBUM_LENGTH);
for (auto item : batchItems) {
albumIds.emplace_back(item.albumId);
}
vector<AlbumChangeData> albumChangeDatas;
albumChangeDatas.reserve(MAX_AFFECTED_ALBUM_LENGTH);
PrepareAlbumChangeForNotify(albumChangeDatas, albumIds);
albumRefresh_.NotifyAnalysisAlbumChange(albumChangeDatas);
batchItems.clear();
}
void AnalysisAlbumRefreshExecution::ConcludeAlbumRefreshValues(const UpdateAlbumData &base,
const AnalysisAlbumRefreshInfo &info, std::string &newCover, int32_t &newCount)
{
newCount = base.albumCount + info.deltaCount_;
if (newCount < 0) {
newCount = 0;
}
if (info.needRefreshCover_) {
newCover = info.refreshCover_;
}
if (newCover.empty() && newCount > 0) {
newCover = base.albumCoverUri;
}
}
void AnalysisAlbumRefreshExecution::UpdateNotifyContext(int32_t albumId, bool needNotify)
{
auto iter = albumNotifyCtxMap_.find(albumId);
if (iter != albumNotifyCtxMap_.end()) {
MEDIA_INFO_LOG("Duplicate album, refresh notify context");
iter->second.needNotify = needNotify;
} else {
NotifyContext ctx;
ctx.needNotify = needNotify;
albumNotifyCtxMap_.emplace(albumId, std::move(ctx));
}
}
void AnalysisAlbumRefreshExecution::RefreshAllAlbum(const vector<string> &albumIdList)
{
MEDIA_INFO_LOG("force update all analysis albums");
auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is null");
AccurateRefresh::AnalysisAlbumAccurateRefresh albumRefresh;
vector<int32_t> affectedAlbumIds;
if (albumIdList.size() < MAX_ANALYSIS_ALBUM_OPERATION_SIZE) {
for (auto albumIdStr : albumIdList) {
CHECK_AND_CONTINUE(MediaFileUtils::IsValidInteger(albumIdStr));
affectedAlbumIds.emplace_back(std::stoi(albumIdStr));
}
albumRefresh.Init(affectedAlbumIds);
}
MediaLibraryRdbUtils::UpdateAnalysisAlbumInternal(rdbStore, albumIdList);
if (albumIdList.size() < MAX_ANALYSIS_ALBUM_OPERATION_SIZE) {
albumRefresh.GenerateDataAfterCustomizedUpdate(affectedAlbumIds);
albumRefresh.Notify();
MEDIA_INFO_LOG("refresh analysis album batch: %{public}zu", albumIdList.size());
} else {
albumRefresh.NotifyForAnalysisAlbumReCheck();
}
}
void AnalysisAlbumRefreshExecution::ResetExecutionStatus()
{
MEDIA_INFO_LOG("AnalysisAlbumRefreshExecution reset status");
assetAlbumMap_.clear();
assetAlbumRefreshMap_.clear();
albumCtxMap_.clear();
groupCtxMap_.clear();
albumNotifyCtxMap_.clear();
}
void AnalysisAlbumRefreshExecution::Notify(vector<PhotoAssetChangeData> &assetChangeDatas)
{
lock_guard<mutex> lock(albumRefreshMtx_);
MEDIA_INFO_LOG("AnalysisAlbumRefreshExecution Notify, datas size: %{public}zu", assetChangeDatas.size());
MediaLibraryTracer tracer;
tracer.Start("AnalysisAlbumRefreshExecution::Notify");
vector<PhotoAssetChangeData> preparedAssetChangeDatas;
preparedAssetChangeDatas.reserve(assetChangeDatas.size());
PreparePhotoChangeForNotify(assetChangeDatas, preparedAssetChangeDatas);
albumRefresh_.NotifyAnalysisPhotoChange(preparedAssetChangeDatas);
vector<AlbumChangeData> albumChangeDatas;
PrepareAlbumChangeForNotify(albumChangeDatas);
albumRefresh_.NotifyAnalysisAlbumChange(albumChangeDatas);
albumNotifyCtxMap_.clear();
}
void AnalysisAlbumRefreshExecution::NotifyAssetForReCheck()
{
albumRefresh_.NotifyForAnalysisAssetReCheck();
}
void AnalysisAlbumRefreshExecution::PreparePhotoChangeForNotify(vector<PhotoAssetChangeData> &assetChangeDatas,
vector<PhotoAssetChangeData> &preparedAssetChangeDatas)
{
preparedAssetChangeDatas.clear();
for (auto &data : assetChangeDatas) {
int32_t fileId = data.GetFileId();
CHECK_AND_CONTINUE(fileId != INVALID_INT32_VALUE);
auto it = assetAlbumRefreshMap_.find(fileId);
CHECK_AND_CONTINUE_ERR_LOG(it != assetAlbumRefreshMap_.end(), "No refresh info, fileId: %{public}d", fileId);
const auto &beforeSet = it->second.first;
PrepareAlbumChangeInfos(beforeSet, false, data.infoBeforeChange_.albumChangeInfos_);
const auto &afterSet = it->second.second;
PrepareAlbumChangeInfos(afterSet, true, data.infoAfterChange_.albumChangeInfos_);
bool hasValidAnalysisAlbumChange = !data.infoBeforeChange_.albumChangeInfos_.empty() ||
!data.infoAfterChange_.albumChangeInfos_.empty();
bool hasValidAsset = AlbumAssetHelper::IsCommonSystemAsset(data.infoBeforeChange_, false) ||
AlbumAssetHelper::IsCommonSystemAsset(data.infoAfterChange_, false);
if (hasValidAnalysisAlbumChange && hasValidAsset) {
preparedAssetChangeDatas.emplace_back(data);
}
}
}
void AnalysisAlbumRefreshExecution::PrepareAlbumChangeInfos(const AlbumIdSet &albumIds,
bool isAfterChange, std::vector<std::shared_ptr<AlbumChangeInfo>> &albumChangeInfos)
{
CHECK_AND_RETURN_LOG(!albumIds.empty(), "No related album to be handled, isAfterChange: %{public}d", isAfterChange);
albumChangeInfos.reserve(albumIds.size());
for (const auto &albumId : albumIds) {
AlbumChangeInfo info = isAfterChange ?
GenerateAlbumChangeInfoAfterChange(albumId) : GenerateAlbumChangeInfoBeforeChange(albumId);
CHECK_AND_CONTINUE_ERR_LOG(info.albumId_ != INVALID_INT32_VALUE,
"No invalid info, albumId: %{public}d", albumId);
albumChangeInfos.emplace_back(make_shared<AlbumChangeInfo>(info));
}
}
AlbumChangeInfo AnalysisAlbumRefreshExecution::GenerateAlbumChangeInfoBase(
int32_t albumId, const UpdateAlbumData &baseInfo)
{
AlbumChangeInfo info;
info.albumId_ = albumId;
info.albumType_ = PhotoAlbumType::SMART;
info.albumSubType_ = baseInfo.albumSubtype;
info.albumName_ = baseInfo.albumName;
info.albumUri_ = MediaFileUtils::GetUriByExtrConditions(
PhotoAlbumColumns::ALBUM_URI_PREFIX, std::to_string(albumId));
return info;
}
AlbumChangeInfo AnalysisAlbumRefreshExecution::GenerateAlbumChangeInfoBeforeChange(int32_t albumId)
{
AlbumChangeInfo info;
CHECK_AND_RETURN_RET_LOG(CheckAlbumNotifyStatus(albumId), info, "Check failed");
auto &ctx = albumCtxMap_[albumId];
UpdateAlbumData &baseInfo = ctx.baseInfo;
info = GenerateAlbumChangeInfoBase(albumId, baseInfo);
info.count_ = baseInfo.albumCount;
info.coverUri_ = baseInfo.albumCoverUri;
info.isCoverChange_ = false;
return info;
}
AlbumChangeInfo AnalysisAlbumRefreshExecution::GenerateAlbumChangeInfoAfterChange(int32_t albumId)
{
AlbumChangeInfo info;
CHECK_AND_RETURN_RET_DEBUG_LOG(CheckAlbumNotifyStatus(albumId), info, "Check failed");
auto &ctx = albumCtxMap_[albumId];
UpdateAlbumData &baseInfo = ctx.baseInfo;
AnalysisAlbumRefreshInfo &refreshInfo = ctx.refreshInfo;
info = GenerateAlbumChangeInfoBase(albumId, baseInfo);
info.count_ = baseInfo.albumCount + refreshInfo.deltaCount_;
if (info.count_ < 0) {
info.count_ = 0;
}
info.coverUri_ = refreshInfo.needRefreshCover_ ? refreshInfo.refreshCover_ : baseInfo.albumCoverUri;
info.isCoverChange_ = (baseInfo.albumCoverUri != refreshInfo.refreshCover_);
return info;
}
bool AnalysisAlbumRefreshExecution::CheckAlbumNotifyStatus(int32_t albumId)
{
auto iter = albumCtxMap_.find(albumId);
CHECK_AND_RETURN_RET_LOG(iter != albumCtxMap_.end(), false,
"No valid album detail recorded, albumId: %{public}d", albumId);
auto notifyMapIter = albumNotifyCtxMap_.find(albumId);
CHECK_AND_RETURN_RET_DEBUG_LOG(notifyMapIter != albumNotifyCtxMap_.end(), false,
"No valid notify detail recorded, albumId: %{public}d", albumId);
CHECK_AND_RETURN_RET_LOG(notifyMapIter->second.needNotify, false,
"No need to notify, albumId: %{public}d", albumId);
return true;
}
void AnalysisAlbumRefreshExecution::PrepareAlbumChangeForNotify(vector<AlbumChangeData> &albumChangeDatas,
const vector<int32_t> &albumIds)
{
if (!albumIds.empty()) {
for (auto albumId : albumIds) {
ProcessAlbumForNotify(albumId, albumChangeDatas);
}
} else {
for (auto &[albumId, actx] : albumCtxMap_) {
ProcessAlbumForNotify(albumId, albumChangeDatas);
}
}
}
void AnalysisAlbumRefreshExecution::ProcessAlbumForNotify(int32_t albumId, vector<AlbumChangeData> &albumChangeDatas)
{
auto iter = albumCtxMap_.find(albumId);
CHECK_AND_RETURN_INFO_LOG(iter != albumCtxMap_.end(),
"No valid album detail recorded, albumId: %{public}d", albumId);
auto notifyMapIter = albumNotifyCtxMap_.find(albumId);
CHECK_AND_RETURN_DEBUG_LOG(notifyMapIter != albumNotifyCtxMap_.end(),
"No valid notify detail recorded, albumId: %{public}d", albumId);
CHECK_AND_RETURN(!notifyMapIter->second.hasNotified);
if (!notifyMapIter->second.needNotify) {
notifyMapIter->second.hasNotified = true;
return;
}
AlbumChangeData data;
data.infoBeforeChange_ = GenerateAlbumChangeInfoBeforeChange(albumId);
data.infoAfterChange_ = GenerateAlbumChangeInfoAfterChange(albumId);
bool noCountChange = data.infoBeforeChange_.count_ == data.infoAfterChange_.count_;
bool noCoverChange = data.infoBeforeChange_.coverUri_ == data.infoAfterChange_.coverUri_;
if (noCountChange && noCoverChange) {
notifyMapIter->second.hasNotified = true;
return;
}
data.operation_ = RDB_OPERATION_UPDATE;
data.version_ = MediaFileUtils::UTCTimeMilliSeconds();
albumChangeDatas.emplace_back(std::move(data));
notifyMapIter->second.hasNotified = true;
}
}
}
}