1 /*
2 * Copyright (c) 2023 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 #include "theme_pack_resource.h"
16
17 #include "hilog_wrapper.h"
18 #include "utils/utils.h"
19 #include <dirent.h>
20 #include <tuple>
21 namespace OHOS {
22 namespace Global {
23 namespace Resource {
24 constexpr int FIRST_ELEMENT = 0;
25 constexpr int SECOND_ELEMENT = 1;
26 constexpr int THIRED_ELEMENT = 2;
27 ThemeResource *ThemeResource::themeRes = nullptr;
ThemeResource(std::string path)28 ThemeResource::ThemeResource(std::string path) : themePath_(path)
29 {
30 themeRes = this;
31 }
32
~ThemeResource()33 ThemeResource::~ThemeResource()
34 {
35 themeValueVec_.clear();
36 iconValues_.clear();
37 }
38
~ThemeQualifierValue()39 ThemeResource::ThemeQualifierValue::~ThemeQualifierValue()
40 {}
41
~ThemeValue()42 ThemeResource::ThemeValue::~ThemeValue()
43 {
44 limitPaths_.clear();
45 }
46
ThemeQualifierValue(const ThemeKey themeKey,std::shared_ptr<ThemeConfig> themeConfig,const std::string & value)47 ThemeResource::ThemeQualifierValue::ThemeQualifierValue(const ThemeKey themeKey,
48 std::shared_ptr<ThemeConfig> themeConfig, const std::string &value) : themeKey_(themeKey),
49 themeConfig_(themeConfig), resValue_(value)
50 {}
51
52 std::unordered_map<std::string, ResType> themeResTypeMap {
53 {"color", COLOR},
54 {"float", FLOAT},
55 {"media", MEDIA},
56 };
57
GetResKey(const std::string & jsonPath)58 std::string GetResKey(const std::string &jsonPath)
59 {
60 auto lastIndex = jsonPath.rfind('/');
61 auto secondLastIndex = jsonPath.rfind('/', lastIndex - 1);
62 auto thirdLastIndex = jsonPath.rfind('/', secondLastIndex - 1);
63 if (lastIndex == std::string::npos || secondLastIndex == std::string::npos
64 || thirdLastIndex == std::string::npos) {
65 return std::string("");
66 }
67 std::string res = jsonPath.substr(thirdLastIndex + 1, secondLastIndex - thirdLastIndex - 1);
68 return res;
69 }
70
GetThemeConfig(const std::string & iconPath)71 std::shared_ptr<ThemeConfig> GetThemeConfig(const std::string &iconPath)
72 {
73 auto themeConfig = std::make_shared<ThemeConfig>();
74 std::string resKey = GetResKey(iconPath);
75 if (resKey == "dark") {
76 themeConfig->SetThemeColorMode(ColorMode::DARK);
77 } else if (resKey == "horizontal") {
78 themeConfig->SetThemeDirection(Direction::DIRECTION_HORIZONTAL);
79 } else if (resKey == "horizontal-dark") {
80 themeConfig->SetThemeDirection(Direction::DIRECTION_HORIZONTAL);
81 themeConfig->SetThemeColorMode(ColorMode::DARK);
82 } else {
83 // default
84 }
85 return themeConfig;
86 }
87
GetResType(const std::string & resTypeStr)88 ResType GetResType(const std::string &resTypeStr)
89 {
90 ResType resType = ResType::VALUES;
91 if (themeResTypeMap.find(resTypeStr) != themeResTypeMap.end()) {
92 resType = themeResTypeMap[resTypeStr];
93 return resType;
94 }
95 return ResType::VALUES;
96 }
97
InitThemeRes(std::pair<std::string,std::string> bundleInfo,cJSON * root,std::shared_ptr<ThemeConfig> themeConfig,const std::string & resTypeStr)98 void ThemeResource::InitThemeRes(std::pair<std::string, std::string> bundleInfo, cJSON *root,
99 std::shared_ptr<ThemeConfig> themeConfig, const std::string &resTypeStr)
100 {
101 if (root == nullptr) {
102 HILOG_WARN("The json file has not resType = %{public}s", resTypeStr.c_str());
103 return;
104 }
105
106 ResType resType = GetResType(resTypeStr);
107 if (root->type == cJSON_Array) {
108 cJSON *childValue = root->child;
109 while (childValue != nullptr) {
110 cJSON *name = cJSON_GetObjectItem(childValue, "name");
111 if (name == nullptr) {
112 HILOG_WARN("The resource name is not exist in childValue");
113 return;
114 }
115 cJSON *value = cJSON_GetObjectItem(childValue, "value");
116 if (value == nullptr) {
117 HILOG_WARN("The resource value is not exist in childValue");
118 return;
119 }
120 auto themeValue = std::make_shared<ThemeValue>();
121 ThemeKey themeKey = ThemeKey(bundleInfo.first, bundleInfo.second, resType, name->valuestring);
122 auto themeQualifierValue = std::make_shared<ThemeQualifierValue>(themeKey, themeConfig,
123 value->valuestring);
124 themeValue->AddThemeLimitPath(themeQualifierValue);
125 themeValueVec_.emplace_back(std::make_tuple(resType, name->valuestring, themeValue));
126 childValue = childValue->next;
127 }
128 }
129 return;
130 }
131
ParseJson(const std::string & bundleName,const std::string & moduleName,const std::string & jsonPath)132 void ThemeResource::ParseJson(const std::string &bundleName, const std::string &moduleName,
133 const std::string &jsonPath)
134 {
135 size_t len = 0;
136 FILE* pf = std::fopen(jsonPath.c_str(), "r");
137 if (pf == nullptr) {
138 HILOG_ERROR("fopen failed in ParseJson");
139 return;
140 }
141 std::fseek(pf, 0, SEEK_END);
142 len = ftell(pf);
143 std::fseek(pf, 0, SEEK_SET);
144 char *jsonData = (char *)malloc(len + 1);
145 std::fread(jsonData, len, 1, pf);
146 jsonData[len] = '\0';
147 auto themeConfig = GetThemeConfig(jsonPath);
148 cJSON *jsonValue = cJSON_Parse(jsonData);
149 std::pair<std::string, std::string> bundleInfo(bundleName, moduleName);
150 cJSON *floatRoot = cJSON_GetObjectItem(jsonValue, "float");
151 InitThemeRes(bundleInfo, floatRoot, themeConfig, "float");
152
153 cJSON *colorRoot = cJSON_GetObjectItem(jsonValue, "color");
154 InitThemeRes(bundleInfo, colorRoot, themeConfig, "color");
155 free(jsonData);
156 jsonData = nullptr;
157 if (pf != nullptr) {
158 fclose(pf);
159 pf = nullptr;
160 }
161 cJSON_Delete(jsonValue);
162 return;
163 }
164
ParseIcon(const std::string & bundleName,const std::string & moduleName,const std::string & iconPath)165 void ThemeResource::ParseIcon(const std::string &bundleName, const std::string &moduleName,
166 const std::string &iconPath)
167 {
168 auto themeConfig = GetThemeConfig(iconPath);
169 auto pos1 = iconPath.rfind('.');
170 auto pos2 = iconPath.rfind('/');
171 if (pos1 == std::string::npos || pos2 == std::string::npos) {
172 HILOG_ERROR("invalid iconPath = %{public}s in ParseIcon", iconPath.c_str());
173 return;
174 }
175 std::string iconName = iconPath.substr(pos2 + 1, pos1 - pos2 - 1);
176 auto themeValue = std::make_shared<ThemeValue>();
177 ThemeKey themeKey = ThemeKey(bundleName, moduleName, ResType::MEDIA, iconName);
178 auto themeQualifierValue = std::make_shared<ThemeQualifierValue>(themeKey, themeConfig, iconPath);
179 themeValue->AddThemeLimitPath(themeQualifierValue);
180 themeValueVec_.emplace_back(std::make_tuple(ResType::MEDIA, iconName, themeValue));
181 return;
182 }
183
GetThemeValues(const std::pair<std::string,std::string> & bundInfo,const ResType & resType,const std::string & name)184 std::vector<std::shared_ptr<ThemeResource::ThemeValue> > ThemeResource::GetThemeValues(
185 const std::pair<std::string, std::string> &bundInfo, const ResType &resType, const std::string &name)
186 {
187 std::vector<std::shared_ptr<ThemeResource::ThemeValue> > result;
188 if (themeValueVec_.empty()) {
189 return result;
190 }
191
192 for (const auto &themeValue : themeValueVec_) {
193 ResType type = std::get<FIRST_ELEMENT>(themeValue);
194 std::string resName = std::get<SECOND_ELEMENT>(themeValue);
195 if (type == resType && resName == name) {
196 result.emplace_back(std::get<THIRED_ELEMENT>(themeValue));
197 }
198 }
199 return result;
200 }
201
GetFiles(const std::string & strCurrentDir)202 std::vector<std::string> GetFiles(const std::string &strCurrentDir)
203 {
204 std::vector<std::string> vFiles;
205 #if !defined(__WINNT__) && !defined(__IDE_PREVIEW__) && !defined(__ARKUI_CROSS__)
206 DIR *dir;
207 struct dirent *pDir;
208 if ((dir = opendir(strCurrentDir.c_str())) == nullptr) {
209 HILOG_ERROR("opendir failed strCurrentDir = %{public}s", strCurrentDir.c_str());
210 return vFiles;
211 }
212 while ((pDir = readdir(dir)) != nullptr) {
213 if (strcmp(pDir->d_name, ".") == 0 || strcmp(pDir->d_name, "..") == 0) {
214 continue;
215 } else if (pDir->d_type == 8) { // 8 means the file
216 vFiles.emplace_back(strCurrentDir + "/" + pDir->d_name);
217 } else if (pDir->d_type == 4) { // 4 means the dir
218 std::string strNextDir = strCurrentDir + "/" + pDir->d_name;
219 std::vector<std::string> temp = GetFiles(strNextDir);
220 vFiles.insert(vFiles.end(), temp.begin(), temp.end());
221 } else {
222 continue;
223 }
224 }
225 closedir(dir);
226 #endif
227 return vFiles;
228 }
229
GetBundleInfo(const std::string & rootDir,const std::string & path)230 std::tuple<std::string, std::string> GetBundleInfo(const std::string& rootDir, const std::string& path)
231 {
232 if (rootDir.empty() || path.empty()) {
233 return std::tuple<std::string, std::string>("", "");
234 }
235 size_t len = rootDir.size();
236 auto pos = rootDir.rfind('/');
237 if (pos == std::string::npos) {
238 HILOG_ERROR("invalid rootDir = %{public}s in GetBundleInfo", rootDir.c_str());
239 return std::tuple<std::string, std::string>("", "");
240 }
241 std::string bundleName = rootDir.substr(pos + 1);
242 auto pos2 = path.find('/', len + 1);
243 if (pos2 == std::string::npos) {
244 HILOG_ERROR("invalid path = %{public}s in GetBundleInfo", path.c_str());
245 return std::tuple<std::string, std::string>("", "");
246 }
247 std::string moduleName = path.substr(len + 1, pos2 - len -1);
248 std::tuple<std::string, std::string> bundleInfoTuple(bundleName, moduleName);
249 return bundleInfoTuple;
250 }
251
LoadThemeResource(const std::string & rootDir)252 const std::shared_ptr<ThemeResource> ThemeResource::LoadThemeResource(const std::string& rootDir)
253 {
254 if (rootDir.empty()) {
255 HILOG_ERROR("Invalid rootDir in LoadThemeResource = %{public}s", rootDir.c_str());
256 return nullptr;
257 }
258 auto themeResource = std::make_shared<ThemeResource>(rootDir);
259 std::vector<std::string> resPaths = GetFiles(rootDir);
260 for (const auto &path : resPaths) {
261 auto bundleInfo = GetBundleInfo(rootDir, path);
262 auto pos = path.rfind('.');
263 if (pos == std::string::npos) {
264 HILOG_ERROR("invalid resPath = %{public}s", path.c_str());
265 continue;
266 }
267 std::string tail = path.substr(pos + 1);
268 if (tail == "json") {
269 themeRes->ParseJson(std::get<FIRST_ELEMENT>(bundleInfo), std::get<SECOND_ELEMENT>(bundleInfo), path);
270 } else {
271 themeRes->ParseIcon(std::get<FIRST_ELEMENT>(bundleInfo), std::get<SECOND_ELEMENT>(bundleInfo), path);
272 }
273 }
274 return themeResource;
275 }
276
GetThemeResBundleName(const std::string & themePath)277 std::string ThemeResource::GetThemeResBundleName(const std::string &themePath)
278 {
279 auto pos = themePath.rfind('/');
280 if (pos == std::string::npos) {
281 HILOG_ERROR("invalid themePath = %{public}s", themePath.c_str());
282 return std::string("");
283 }
284 std::string bundleName = themePath.substr(pos + 1);
285 return bundleName;
286 }
287
GetIconsBundleName(const std::string & iconPath)288 std::string GetIconsBundleName(const std::string& iconPath)
289 {
290 if (iconPath.empty()) {
291 return std::string("");
292 }
293 auto pos = iconPath.rfind('/');
294 if (pos == std::string::npos) {
295 HILOG_ERROR("invalid iconPath = %{public}s in GetIconsBundleName", iconPath.c_str());
296 return std::string("");
297 }
298 return iconPath.substr(pos + 1);
299 }
300
LoadThemeIconResource(const std::string & iconPath)301 const std::shared_ptr<ThemeResource> ThemeResource::LoadThemeIconResource(const std::string& iconPath)
302 {
303 if (iconPath.empty()) {
304 return nullptr;
305 }
306 auto themeResource = std::make_shared<ThemeResource>(iconPath);
307 std::string bundleName = GetIconsBundleName(iconPath);
308 std::vector<std::string> resPaths = GetFiles(iconPath);
309 for (const auto &path : resPaths) {
310 auto pos1 = path.rfind('.');
311 auto pos2 = path.rfind('/');
312 if (pos1 == std::string::npos || pos2 == std::string::npos) {
313 HILOG_ERROR("invalid path = %{public}s in LoadThemeIconResource", path.c_str());
314 continue;
315 }
316 std::string iconName = path.substr(pos2 + 1, pos1 - pos2 - 1);
317 ThemeKey themeKey = ThemeKey(bundleName, "", ResType::MEDIA, iconName);
318 themeRes->iconValues_.emplace_back(std::make_pair(themeKey, path));
319 }
320 return themeResource;
321 }
322
GetThemeAppIcon(const std::pair<std::string,std::string> & bundleInfo,const std::string & iconName)323 const std::string ThemeResource::GetThemeAppIcon(const std::pair<std::string, std::string> &bundleInfo,
324 const std::string &iconName)
325 {
326 for (size_t i = 0; i < iconValues_.size(); i++) {
327 if (iconValues_[i].first.bundleName != bundleInfo.first) {
328 continue;
329 }
330 if (iconName == iconValues_[i].first.resName) {
331 return iconValues_[i].second;
332 }
333 }
334 return std::string("");
335 }
336 } // namespace Resource
337 } // namespace Global
338 } // namespace OHOS
339