• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 
16 #include <ani.h>
17 #include <iostream>
18 #include <array>
19 #include "ani_error.h"
20 #include "ani_class_name.h"
21 #include "media_assets_change_request_ani.h"
22 #include "media_log.h"
23 #include "userfile_client.h"
24 #include "medialibrary_ani_log.h"
25 
26 using namespace std;
27 using namespace OHOS::DataShare;
28 
29 namespace OHOS::Media {
30 
31 constexpr int32_t YES = 1;
32 constexpr int32_t NO = 0;
33 
34 std::vector<std::shared_ptr<FileAsset>> MediaAssetsChangeRequestAni::fileAssets_;
35 std::vector<AssetsChangeOperation> MediaAssetsChangeRequestAni::assetsChangeOperations_;
36 bool MediaAssetsChangeRequestAni::isFavorite_;
37 bool MediaAssetsChangeRequestAni::isHidden_;
38 
MediaAssetsChangeRequestAni(vector<shared_ptr<FileAsset>> fileAssets)39 MediaAssetsChangeRequestAni::MediaAssetsChangeRequestAni(vector<shared_ptr<FileAsset>> fileAssets)
40 {
41     fileAssets_ = fileAssets;
42 }
43 
~MediaAssetsChangeRequestAni()44 MediaAssetsChangeRequestAni::~MediaAssetsChangeRequestAni()
45 {
46     assetsChangeOperations_.clear();
47     fileAssets_.clear();
48 }
49 
RecordChangeOperation(AssetsChangeOperation changeOperation)50 void MediaAssetsChangeRequestAni::RecordChangeOperation(AssetsChangeOperation changeOperation)
51 {
52     assetsChangeOperations_.push_back(changeOperation);
53 }
54 
SetFavorite(ani_env * env,ani_object object,ani_boolean isFavorite)55 void MediaAssetsChangeRequestAni::SetFavorite([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object,
56     ani_boolean isFavorite)
57 {
58     if (!MediaLibraryAniUtils::IsSystemApp()) {
59         AniError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
60         return;
61     }
62     auto context =  unwrapp(env, object);
63     if (context == nullptr) {
64         return;
65     }
66     for (const auto& fileAsset : context->fileAssets_) {
67         fileAsset->SetFavorite(isFavorite);
68     }
69     RecordChangeOperation(AssetsChangeOperation::BATCH_SET_FAVORITE);
70 }
71 
SetHidden(ani_env * env,ani_object object,ani_boolean isHidden)72 void MediaAssetsChangeRequestAni::SetHidden([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object,
73     ani_boolean isHidden)
74 {
75     if (!MediaLibraryAniUtils::IsSystemApp()) {
76         AniError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
77         return;
78     }
79     auto context = unwrapp(env, object);
80     if (context == nullptr) {
81         return;
82     }
83     for (const auto& fileAsset : context->fileAssets_) {
84         fileAsset->SetHidden(isHidden);
85     }
86     RecordChangeOperation(AssetsChangeOperation::BATCH_SET_HIDDEN);
87 }
88 
SetAssetsPropertyExecute(const AssetsChangeOperation & changeOperation)89 bool MediaAssetsChangeRequestAni::SetAssetsPropertyExecute(const AssetsChangeOperation& changeOperation)
90 {
91     string uri;
92     DataShare::DataSharePredicates predicates;
93     DataShare::DataShareValuesBucket valuesBucket;
94     predicates.In(PhotoColumn::MEDIA_ID, GetFileAssetUriArray());
95     switch (changeOperation) {
96         case AssetsChangeOperation::BATCH_SET_FAVORITE:
97             uri = PAH_BATCH_UPDATE_FAVORITE;
98             valuesBucket.Put(PhotoColumn::MEDIA_IS_FAV, GetFavoriteStatus() ? YES : NO);
99             MEDIA_INFO_LOG("Batch set favorite: %{public}d", GetFavoriteStatus() ? YES : NO);
100             break;
101         case AssetsChangeOperation::BATCH_SET_HIDDEN:
102             uri = PAH_HIDE_PHOTOS;
103             valuesBucket.Put(PhotoColumn::MEDIA_HIDDEN, GetHiddenStatus() ? YES : NO);
104             break;
105         default:
106             MEDIA_ERR_LOG("Unsupported assets change operation: %{public}d", changeOperation);
107             return false;
108     }
109 
110     MEDIA_INFO_LOG("changeOperation:%{public}d, size:%{public}zu",
111         changeOperation, GetFileAssetUriArray().size());
112     MediaLibraryAniUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
113     Uri updateAssetsUri(uri);
114     int32_t changedRows = UserFileClient::Update(updateAssetsUri, predicates, valuesBucket);
115     if (changedRows < 0) {
116         MEDIA_ERR_LOG("Failed to set property, operation: %{public}d, err: %{public}d", changeOperation, changedRows);
117         return false;
118     }
119     return true;
120 }
121 
ApplyChanges(ani_env * env,ani_object aniObject)122 ani_status MediaAssetsChangeRequestAni::ApplyChanges(ani_env *env, ani_object aniObject)
123 {
124     auto context =  unwrapp(env, aniObject);
125     if (context == nullptr) {
126         return ANI_ERROR;
127     }
128     ANI_CHECK_RETURN_RET_LOG(context->assetsChangeOperations_.empty() == false, ANI_ERROR,
129         "MediaAssetsChangeRequestAni::ApplyChanges assetsChangeOperations_ is empty");
130     ANI_CHECK_RETURN_RET_LOG(context->fileAssets_.empty() == false, ANI_ERROR,
131         "MediaAssetsChangeRequestAni::ApplyChanges fileAssets_ is empty");
132     for (const auto& fileAsset : context->fileAssets_) {
133         ANI_CHECK_RETURN_RET_LOG(fileAsset != nullptr && fileAsset->GetId() > 0 && fileAsset->GetUri().empty() == false,
134             ANI_ERROR, "MediaAssetsChangeRequestAni::ApplyChanges check fileAssets_ failed");
135     }
136 
137     unordered_set<AssetsChangeOperation> appliedOperations;
138     for (const auto& changeOperation : assetsChangeOperations_) {
139         // Keep the final result(s) of each operation, and commit only once.
140         if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
141             continue;
142         }
143 
144         bool valid = MediaAssetsChangeRequestAni::SetAssetsPropertyExecute(changeOperation);
145         if (!valid) {
146             MEDIA_ERR_LOG("Failed to apply assets change request, operation: %{public}d", changeOperation);
147             return ANI_ERROR;
148         }
149         appliedOperations.insert(changeOperation);
150     }
151     return ANI_OK;
152 }
153 
GetFileAssetUriArray()154 vector<string> MediaAssetsChangeRequestAni::GetFileAssetUriArray()
155 {
156     vector<string> uriArray;
157     uriArray.reserve(fileAssets_.size());
158     for (const auto& fileAsset : fileAssets_) {
159         uriArray.push_back(fileAsset->GetUri());
160     }
161     return uriArray;
162 }
163 
GetFavoriteStatus()164 bool MediaAssetsChangeRequestAni::GetFavoriteStatus()
165 {
166     return isFavorite_;
167 }
168 
GetHiddenStatus()169 bool MediaAssetsChangeRequestAni::GetHiddenStatus()
170 {
171     return isHidden_;
172 }
173 
174 
unwrapp(ani_env * env,ani_object object)175 MediaAssetsChangeRequestAni* MediaAssetsChangeRequestAni::unwrapp(ani_env *env, ani_object object)
176 {
177     ani_long context;
178     if (ANI_OK != env->Object_GetFieldByName_Long(object, "nativeMediaAssetsChangeRequestHandleImpl", &context)) {
179         ANI_ERR_LOG("unwrapp err");
180         return nullptr;
181     }
182     return reinterpret_cast<MediaAssetsChangeRequestAni *>(context);
183 }
184 
create(ani_env * env,ani_class clazz)185 ani_object MediaAssetsChangeRequestAni::create([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz)
186 {
187     std::shared_ptr<FileAsset> fileAsset = std::make_shared<FileAsset>();
188     vector<shared_ptr<FileAsset>> fileAssets = {fileAsset};
189     auto nativeMediaAssetsChangeRequestHandle = std::make_unique<MediaAssetsChangeRequestAni>(fileAssets);
190 
191     ani_class cls;
192     if (ANI_OK != env->FindClass(ANI_CLASS_MEDIA_ASSETS_CHANGE_REQUEST.c_str(), &cls)) {
193         ANI_ERR_LOG("Failed to find class: %s", ANI_CLASS_MEDIA_ASSETS_CHANGE_REQUEST.c_str());
194         return nullptr;
195     }
196 
197     ani_method ctor;
198     if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", "J:V", &ctor)) {
199         ANI_ERR_LOG("Failed to find MediaAssetsChangeRequest constructor method");
200         return nullptr;
201     }
202 
203     ani_object context_object;
204     if (ANI_OK != env->Object_New(cls, ctor, &context_object,
205         reinterpret_cast<ani_long>(nativeMediaAssetsChangeRequestHandle.get()))) {
206         ANI_ERR_LOG("Failed to create MediaAssetsChangeRequest object");
207         return nullptr;
208     }
209 
210     nativeMediaAssetsChangeRequestHandle.release();
211     return context_object;
212 }
213 
MediaAssetsChangeRequestAniInit(ani_env * env)214 ani_status MediaAssetsChangeRequestAni::MediaAssetsChangeRequestAniInit(ani_env *env)
215 {
216     ani_class cls;
217     if (ANI_OK != env->FindClass(ANI_CLASS_MEDIA_ASSETS_CHANGE_REQUEST.c_str(), &cls)) {
218         return ANI_ERROR;
219     }
220 
221     std::array methods = {
222         ani_native_function {"SetFavoriteSync", nullptr,
223             reinterpret_cast<void *>(MediaAssetsChangeRequestAni::SetFavorite)},
224         ani_native_function {"SetHiddenSync",
225             nullptr, reinterpret_cast<void *>(MediaAssetsChangeRequestAni::SetHidden)},
226         ani_native_function {"create", nullptr, reinterpret_cast<void *>(MediaAssetsChangeRequestAni::create)},
227 
228     };
229 
230     if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
231         return ANI_ERROR;
232     };
233 
234     return ANI_OK;
235 }
236 } // namespace OHOS::MEDIA