1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #define MLOG_TAG "PhotoAlbumLPathOperation"
16
17 #include "photo_album_lpath_operation.h"
18
19 #include <algorithm>
20 #include <numeric>
21
22 #include "media_log.h"
23 #include "medialibrary_errno.h"
24 #include "userfile_manager_types.h"
25 #include "media_file_utils.h"
26 #include "result_set_utils.h"
27 #include "photo_album_merge_operation.h"
28
29 namespace OHOS::Media {
30 std::shared_ptr<PhotoAlbumLPathOperation> PhotoAlbumLPathOperation::instance_ = nullptr;
31 std::mutex PhotoAlbumLPathOperation::objMutex_;
32
GetInstance()33 PhotoAlbumLPathOperation &PhotoAlbumLPathOperation::GetInstance()
34 {
35 std::lock_guard<std::mutex> lock(PhotoAlbumLPathOperation::objMutex_);
36 if (PhotoAlbumLPathOperation::instance_ == nullptr) {
37 PhotoAlbumLPathOperation::instance_ = std::make_shared<PhotoAlbumLPathOperation>();
38 }
39 return *PhotoAlbumLPathOperation::instance_;
40 }
41
Start()42 PhotoAlbumLPathOperation &PhotoAlbumLPathOperation::Start()
43 {
44 this->isContinue_.store(true);
45 return *this;
46 }
47
Stop()48 void PhotoAlbumLPathOperation::Stop()
49 {
50 this->isContinue_.store(false);
51 }
52
SetRdbStore(const std::shared_ptr<MediaLibraryRdbStore> & rdbStorePtr)53 PhotoAlbumLPathOperation &PhotoAlbumLPathOperation::SetRdbStore(
54 const std::shared_ptr<MediaLibraryRdbStore> &rdbStorePtr)
55 {
56 this->rdbStorePtr_ = rdbStorePtr;
57 return *this;
58 }
59
GetAlbumAffectedCount() const60 int32_t PhotoAlbumLPathOperation::GetAlbumAffectedCount() const
61 {
62 return this->albumAffectedCount_;
63 }
64
CleanInvalidPhotoAlbums()65 PhotoAlbumLPathOperation &PhotoAlbumLPathOperation::CleanInvalidPhotoAlbums()
66 {
67 if (!this->isContinue_.load()) {
68 MEDIA_INFO_LOG("Media_Operation: clean invalid album operation is not allowed.");
69 return *this;
70 }
71 std::vector<PhotoAlbumInfoPo> invalidAlbumList = this->GetInvalidPhotoAlbums();
72 if (invalidAlbumList.empty()) {
73 MEDIA_INFO_LOG("Media_Operation: no invalid album found.");
74 return *this;
75 }
76 // Log the invalid albums
77 int32_t index = 0;
78 int32_t total = static_cast<int32_t>(invalidAlbumList.size());
79 for (const auto &albumInfo : invalidAlbumList) {
80 MEDIA_INFO_LOG("Media_Operation: clean invalid album! index: %{public}d / %{public}d, Object: %{public}s",
81 ++index,
82 total,
83 albumInfo.ToString().c_str());
84 }
85 this->albumAffectedCount_ += total;
86 // Delete the invalid albums
87 std::string sql = this->SQL_PHOTO_ALBUM_EMPTY_DELETE;
88 int32_t ret = this->rdbStorePtr_->ExecuteSql(sql);
89 if (ret != NativeRdb::E_OK) {
90 MEDIA_ERR_LOG("Media_Operation: Failed to exec: %{public}s", sql.c_str());
91 }
92 MEDIA_INFO_LOG("Media_Operation: clean invalid album completed! "
93 "total: %{public}d, ret: %{public}d",
94 total,
95 ret);
96 return *this;
97 }
98
GetInvalidPhotoAlbums()99 std::vector<PhotoAlbumInfoPo> PhotoAlbumLPathOperation::GetInvalidPhotoAlbums()
100 {
101 if (this->rdbStorePtr_ == nullptr) {
102 return {};
103 }
104 std::string querySql = this->SQL_PHOTO_ALBUM_EMPTY_QUERY;
105 auto resultSet = this->rdbStorePtr_->QuerySql(querySql);
106 if (resultSet == nullptr) {
107 return {};
108 }
109 std::vector<PhotoAlbumInfoPo> result;
110 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
111 result.emplace_back(PhotoAlbumInfoPo().Parse(resultSet));
112 }
113 return result;
114 }
115
CleanDuplicatePhotoAlbums()116 PhotoAlbumLPathOperation &PhotoAlbumLPathOperation::CleanDuplicatePhotoAlbums()
117 {
118 if (!this->isContinue_.load()) {
119 MEDIA_INFO_LOG("Media_Operation: clean invalid album operation is not allowed.");
120 return *this;
121 }
122 std::vector<PhotoAlbumInfoPo> mainAlbumInfoList = this->GetDuplicatelPathAlbumInfoMain();
123 if (mainAlbumInfoList.empty()) {
124 MEDIA_INFO_LOG("Media_Operation: no duplicate album found.");
125 return *this;
126 }
127 // Execute & Log the duplicate albums
128 int index = 0;
129 int32_t total = static_cast<int32_t>(mainAlbumInfoList.size());
130 for (const auto &albumInfo : mainAlbumInfoList) {
131 int32_t ret = this->CleanDuplicatePhotoAlbum(albumInfo);
132 MEDIA_INFO_LOG("Media_Operation: clean duplicate album (MAIN) completed! "
133 "isContinue: %{public}d, index: %{public}d / %{public}d, ret: %{public}d, Object: %{public}s",
134 this->isContinue_.load(),
135 ++index,
136 total,
137 ret,
138 albumInfo.ToString().c_str());
139 if (!this->isContinue_.load()) {
140 break;
141 }
142 }
143 this->albumAffectedCount_ += index;
144 return *this;
145 }
146
CleanDuplicatePhotoAlbum(const PhotoAlbumInfoPo & mainAlbumInfo)147 int32_t PhotoAlbumLPathOperation::CleanDuplicatePhotoAlbum(const PhotoAlbumInfoPo &mainAlbumInfo)
148 {
149 bool isInvalid = mainAlbumInfo.albumId <= 0 || mainAlbumInfo.lPath.empty() || mainAlbumInfo.albumName.empty();
150 if (isInvalid) {
151 return NativeRdb::E_OK;
152 }
153 std::vector<PhotoAlbumInfoPo> subAlbumInfoList = this->GetDuplicatelPathAlbumInfoSub(mainAlbumInfo);
154 if (subAlbumInfoList.empty()) {
155 return NativeRdb::E_OK;
156 }
157 int32_t index = 0;
158 int32_t total = static_cast<int32_t>(subAlbumInfoList.size());
159 for (const auto &subAlbumInfo : subAlbumInfoList) {
160 index++;
161 bool isInvalidSub = subAlbumInfo.albumId <= 0 || subAlbumInfo.lPath.empty() || subAlbumInfo.albumName.empty();
162 isInvalidSub = isInvalidSub || this->MergePhotoAlbum(mainAlbumInfo, subAlbumInfo) != NativeRdb::E_OK;
163 MEDIA_INFO_LOG("Media_Operation: clean duplicate album (sub) completed! "
164 "index: %{public}d / %{public}d, "
165 "isInvalid: %{public}d, mainAlbum: %{public}s, subAlbum: %{public}s",
166 index,
167 total,
168 isInvalidSub,
169 mainAlbumInfo.ToString().c_str(),
170 subAlbumInfo.ToString().c_str());
171 }
172 return this->UpdateAlbumInfoFromAlbumPluginByAlbumId(mainAlbumInfo);
173 }
174
CleanEmptylPathPhotoAlbums()175 PhotoAlbumLPathOperation &PhotoAlbumLPathOperation::CleanEmptylPathPhotoAlbums()
176 {
177 if (!this->isContinue_.load()) {
178 MEDIA_INFO_LOG("Media_Operation: clean invalid album operation is not allowed.");
179 return *this;
180 }
181 std::vector<PhotoAlbumInfoPo> subAlbumInfoList = this->GetEmptylPathAlbumInfo();
182 if (subAlbumInfoList.empty()) {
183 MEDIA_INFO_LOG("Media_Operation: no empty lPath album found.");
184 return *this;
185 }
186 // Execute & Log the empty albums
187 int32_t index = 0;
188 int32_t total = static_cast<int32_t>(subAlbumInfoList.size());
189 for (const auto &subAlbumInfo : subAlbumInfoList) {
190 index++;
191 int32_t ret = this->CleanEmptylPathPhotoAlbum(subAlbumInfo);
192 MEDIA_INFO_LOG("Media_Operation: clean empty lPath album completed! "
193 "isContinue: %{public}d, index: %{public}d / %{public}d, ret: %{public}d, subAlbum: %{public}s",
194 this->isContinue_.load(),
195 index,
196 total,
197 ret,
198 subAlbumInfo.ToString().c_str());
199 if (!this->isContinue_.load()) {
200 break;
201 }
202 }
203 this->albumAffectedCount_ += index;
204 return *this;
205 }
206
ToString(const std::vector<NativeRdb::ValueObject> & values)207 std::string PhotoAlbumLPathOperation::ToString(const std::vector<NativeRdb::ValueObject> &values)
208 {
209 std::vector<std::string> result;
210 std::string str;
211 for (auto &value : values) {
212 value.GetString(str);
213 result.emplace_back(str + ", ");
214 }
215 return std::accumulate(result.begin(), result.end(), std::string());
216 }
217
UpdateAlbumInfoFromAlbumPluginByAlbumId(const PhotoAlbumInfoPo & albumInfo)218 int32_t PhotoAlbumLPathOperation::UpdateAlbumInfoFromAlbumPluginByAlbumId(const PhotoAlbumInfoPo &albumInfo)
219 {
220 if (this->rdbStorePtr_ == nullptr) {
221 return NativeRdb::E_ERROR;
222 }
223 bool isInvalid = albumInfo.albumId <= 0 || albumInfo.lPath.empty();
224 if (isInvalid) {
225 return NativeRdb::E_ERROR;
226 }
227 std::string sql = this->SQL_PHOTO_ALBUM_SYNC_BUNDLE_NAME_UPDATE;
228 const std::vector<NativeRdb::ValueObject> bindArgs = {
229 albumInfo.lPath, albumInfo.lPath, albumInfo.lPath, albumInfo.albumId, albumInfo.lPath};
230 int32_t ret = this->rdbStorePtr_->ExecuteSql(sql, bindArgs);
231 if (ret != NativeRdb::E_OK) {
232 MEDIA_ERR_LOG("Media_Operation: Failed to exec: %{public}s, bindArgs: %{public}s",
233 sql.c_str(),
234 this->ToString(bindArgs).c_str());
235 return ret;
236 }
237 return NativeRdb::E_OK;
238 }
239
UpdateAlbumLPathByAlbumId(const PhotoAlbumInfoPo & albumInfo)240 int32_t PhotoAlbumLPathOperation::UpdateAlbumLPathByAlbumId(const PhotoAlbumInfoPo &albumInfo)
241 {
242 if (this->rdbStorePtr_ == nullptr) {
243 return NativeRdb::E_ERROR;
244 }
245 bool isInvalid = albumInfo.lPath.empty() || albumInfo.albumId <= 0;
246 if (isInvalid) {
247 return NativeRdb::E_ERROR;
248 }
249 std::string sql = this->SQL_PHOTO_ALBUM_UPDATE_LPATH_BY_ALBUM_ID;
250 const std::vector<NativeRdb::ValueObject> bindArgs = {albumInfo.lPath, albumInfo.albumId, albumInfo.lPath};
251 int32_t ret = this->rdbStorePtr_->ExecuteSql(sql, bindArgs);
252 if (ret != NativeRdb::E_OK) {
253 MEDIA_ERR_LOG("Media_Operation: Failed to exec: %{public}s, bindArgs: %{public}s",
254 sql.c_str(),
255 this->ToString(bindArgs).c_str());
256 return ret;
257 }
258 return NativeRdb::E_OK;
259 }
260
GetLatestAlbumInfoBylPath(const std::string & lPath)261 PhotoAlbumInfoPo PhotoAlbumLPathOperation::GetLatestAlbumInfoBylPath(const std::string &lPath)
262 {
263 if (this->rdbStorePtr_ == nullptr || lPath.empty()) {
264 return PhotoAlbumInfoPo();
265 }
266 std::string sql = this->SQL_PHOTO_ALBUM_QUERY_BY_LPATH;
267 const std::vector<NativeRdb::ValueObject> bindArgs = {lPath};
268 auto resultSet = this->rdbStorePtr_->QuerySql(sql, bindArgs);
269 if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
270 return PhotoAlbumInfoPo();
271 }
272 return PhotoAlbumInfoPo().Parse(resultSet);
273 }
274
CleanEmptylPathPhotoAlbum(const PhotoAlbumInfoPo & subAlbumInfo)275 int32_t PhotoAlbumLPathOperation::CleanEmptylPathPhotoAlbum(const PhotoAlbumInfoPo &subAlbumInfo)
276 {
277 bool isInvalid = subAlbumInfo.albumId <= 0 || subAlbumInfo.lPath.empty() || subAlbumInfo.albumName.empty();
278 if (isInvalid) {
279 return NativeRdb::E_ERROR;
280 }
281 PhotoAlbumInfoPo mainAlbumInfo = this->GetLatestAlbumInfoBylPath(subAlbumInfo.lPath);
282 bool isMainAlbumInvalid =
283 mainAlbumInfo.albumId <= 0 || mainAlbumInfo.lPath.empty() || mainAlbumInfo.albumName.empty();
284 bool isSuccessed = false;
285 if (!isMainAlbumInvalid) {
286 isSuccessed = this->MergePhotoAlbum(mainAlbumInfo, subAlbumInfo) == NativeRdb::E_OK;
287 } else {
288 isSuccessed = this->UpdateAlbumLPathByAlbumId(subAlbumInfo) == NativeRdb::E_OK;
289 mainAlbumInfo = subAlbumInfo;
290 }
291 isSuccessed = isSuccessed && this->UpdateAlbumInfoFromAlbumPluginByAlbumId(mainAlbumInfo) == NativeRdb::E_OK;
292 MEDIA_INFO_LOG("Media_Operation: clean empty lPath album (sub) completed! "
293 "isSuccessed: %{public}d, mainAlbum: %{public}s, subAlbum: %{public}s",
294 isSuccessed,
295 mainAlbumInfo.ToString().c_str(),
296 subAlbumInfo.ToString().c_str());
297 return isSuccessed ? NativeRdb::E_OK : NativeRdb::E_ERROR;
298 }
299
MergePhotoAlbum(const PhotoAlbumInfoPo & mainAlbumInfo,const PhotoAlbumInfoPo & subAlbumInfo)300 int32_t PhotoAlbumLPathOperation::MergePhotoAlbum(
301 const PhotoAlbumInfoPo &mainAlbumInfo, const PhotoAlbumInfoPo &subAlbumInfo)
302 {
303 bool isInvalid = this->rdbStorePtr_ == nullptr || mainAlbumInfo.albumId <= 0 || subAlbumInfo.albumId <= 0;
304 if (isInvalid) {
305 return NativeRdb::E_ERROR;
306 }
307 return PhotoAlbumMergeOperation()
308 .SetRdbStore(this->rdbStorePtr_)
309 .MergeAlbum(subAlbumInfo.albumId, mainAlbumInfo.albumId);
310 }
311
GetDuplicatelPathAlbumInfoMain()312 std::vector<PhotoAlbumInfoPo> PhotoAlbumLPathOperation::GetDuplicatelPathAlbumInfoMain()
313 {
314 if (this->rdbStorePtr_ == nullptr) {
315 return {};
316 }
317 std::string querySql = this->SQL_PHOTO_ALBUM_DUPLICATE_LPATH_MAIN_QUERY;
318 auto resultSet = this->rdbStorePtr_->QuerySql(querySql);
319 if (resultSet == nullptr) {
320 return {};
321 }
322 std::vector<PhotoAlbumInfoPo> result;
323 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
324 result.emplace_back(PhotoAlbumInfoPo().Parse(resultSet));
325 }
326 return result;
327 }
328
GetDuplicatelPathAlbumInfoSub(const PhotoAlbumInfoPo & albumInfo)329 std::vector<PhotoAlbumInfoPo> PhotoAlbumLPathOperation::GetDuplicatelPathAlbumInfoSub(const PhotoAlbumInfoPo &albumInfo)
330 {
331 bool isInvalid = this->rdbStorePtr_ == nullptr || albumInfo.albumId <= 0 || albumInfo.lPath.empty();
332 if (isInvalid) {
333 return {};
334 }
335 std::string sql = this->SQL_PHOTO_ALBUM_DUPLICATE_LPATH_SUB_QUERY;
336 const std::vector<NativeRdb::ValueObject> bindArgs = {albumInfo.albumId, albumInfo.lPath};
337 auto resultSet = this->rdbStorePtr_->QuerySql(sql, bindArgs);
338 if (resultSet == nullptr) {
339 return {};
340 }
341 std::vector<PhotoAlbumInfoPo> result;
342 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
343 result.emplace_back(PhotoAlbumInfoPo().Parse(resultSet));
344 }
345 return result;
346 }
347
GetEmptylPathAlbumInfo()348 std::vector<PhotoAlbumInfoPo> PhotoAlbumLPathOperation::GetEmptylPathAlbumInfo()
349 {
350 if (this->rdbStorePtr_ == nullptr) {
351 return {};
352 }
353 std::string querySql = this->SQL_PHOTO_ALBUM_FIX_LPATH_QUERY;
354 auto resultSet = this->rdbStorePtr_->QuerySql(querySql);
355 if (resultSet == nullptr) {
356 return {};
357 }
358 std::vector<PhotoAlbumInfoPo> result;
359 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
360 result.emplace_back(PhotoAlbumInfoPo().Parse(resultSet));
361 }
362 return result;
363 }
364 } // namespace OHOS::Media