• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 const std::string DYNAMIC_ICON = "dynamic_icons";
ThemeResource(std::string path)28 ThemeResource::ThemeResource(std::string path) : themePath_(path)
29 {}
30 
~ThemeResource()31 ThemeResource::~ThemeResource()
32 {
33     themeValueVec_.clear();
34     iconValues_.clear();
35 }
36 
~ThemeQualifierValue()37 ThemeResource::ThemeQualifierValue::~ThemeQualifierValue()
38 {}
39 
~ThemeValue()40 ThemeResource::ThemeValue::~ThemeValue()
41 {
42     limitPaths_.clear();
43 }
44 
ThemeQualifierValue(const ThemeKey themeKey,std::shared_ptr<ThemeConfig> themeConfig,const std::string & value)45 ThemeResource::ThemeQualifierValue::ThemeQualifierValue(const ThemeKey themeKey,
46     std::shared_ptr<ThemeConfig> themeConfig, const std::string &value) : themeKey_(themeKey),
47     themeConfig_(themeConfig), resValue_(value)
48 {}
49 
50 std::unordered_map<std::string, ResType> themeResTypeMap {
51     {"color", COLOR},
52     {"float", FLOAT},
53     {"media", MEDIA},
54 };
55 
GetResKey(const std::string & jsonPath)56 std::string GetResKey(const std::string &jsonPath)
57 {
58     auto lastIndex = jsonPath.rfind('/');
59     if (lastIndex < 1) {
60         return std::string("");
61     }
62     auto secondLastIndex = jsonPath.rfind('/', lastIndex - 1);
63     if (secondLastIndex < 1) {
64         return std::string("");
65     }
66     auto thirdLastIndex = jsonPath.rfind('/', secondLastIndex - 1);
67     if (lastIndex == std::string::npos || secondLastIndex == std::string::npos
68         || thirdLastIndex == std::string::npos) {
69         return std::string("");
70     }
71     if (secondLastIndex < thirdLastIndex + 1) {
72         return std::string("");
73     }
74     std::string res = jsonPath.substr(thirdLastIndex + 1, secondLastIndex - thirdLastIndex - 1);
75     return res;
76 }
77 
GetThemeConfig(const std::string & iconPath)78 std::shared_ptr<ThemeConfig> GetThemeConfig(const std::string &iconPath)
79 {
80     auto themeConfig = std::make_shared<ThemeConfig>();
81     std::string resKey = GetResKey(iconPath);
82     if (resKey == "dark") {
83         themeConfig->SetThemeColorMode(ColorMode::DARK);
84     } else if (resKey == "horizontal") {
85         themeConfig->SetThemeDirection(Direction::DIRECTION_HORIZONTAL);
86     } else if (resKey == "horizontal-dark") {
87         themeConfig->SetThemeDirection(Direction::DIRECTION_HORIZONTAL);
88         themeConfig->SetThemeColorMode(ColorMode::DARK);
89     } else {
90         // default
91     }
92     return themeConfig;
93 }
94 
GetResType(const std::string & resTypeStr)95 ResType GetResType(const std::string &resTypeStr)
96 {
97     ResType resType = ResType::VALUES;
98     if (themeResTypeMap.find(resTypeStr) != themeResTypeMap.end()) {
99         resType = themeResTypeMap[resTypeStr];
100         return resType;
101     }
102     return ResType::VALUES;
103 }
104 
InitThemeRes(std::pair<std::string,std::string> bundleInfo,cJSON * root,std::shared_ptr<ThemeConfig> themeConfig,const std::string & resTypeStr)105 void ThemeResource::InitThemeRes(std::pair<std::string, std::string> bundleInfo, cJSON *root,
106     std::shared_ptr<ThemeConfig> themeConfig, const std::string &resTypeStr)
107 {
108     if (root == nullptr) {
109         RESMGR_HILOGD(RESMGR_TAG, "The json file has not resType = %{public}s", resTypeStr.c_str());
110         return;
111     }
112 
113     ResType resType = GetResType(resTypeStr);
114     if (root->type == cJSON_Array) {
115         cJSON *childValue = root->child;
116         while (childValue != nullptr) {
117             if (childValue->type != cJSON_Object) {
118                 childValue = childValue->next;
119                 continue;
120             }
121             cJSON *name = cJSON_GetObjectItem(childValue, "name");
122             if (name == nullptr || !cJSON_IsString(name)) {
123                 RESMGR_HILOGW(RESMGR_TAG, "The resource name is not exist in childValue");
124                 return;
125             }
126             cJSON *value = cJSON_GetObjectItem(childValue, "value");
127             if (value == nullptr || !cJSON_IsString(value)) {
128                 RESMGR_HILOGW(RESMGR_TAG, "The resource value is not exist in childValue");
129                 return;
130             }
131             auto themeValue = std::make_shared<ThemeValue>();
132             ThemeKey themeKey = ThemeKey(bundleInfo.first, bundleInfo.second, resType, name->valuestring);
133             auto themeQualifierValue = std::make_shared<ThemeQualifierValue>(themeKey, themeConfig,
134                 value->valuestring);
135             themeValue->AddThemeLimitPath(themeQualifierValue);
136             themeValueVec_.emplace_back(std::make_tuple(resType, name->valuestring, themeValue));
137             childValue = childValue->next;
138         }
139     }
140     return;
141 }
142 
ReleaseJson(char * jsonData,FILE * pf)143 void ThemeResource::ReleaseJson(char* jsonData, FILE* pf)
144 {
145     if (jsonData != nullptr) {
146         free(jsonData);
147         jsonData = nullptr;
148     }
149 
150     if (pf != nullptr) {
151         fclose(pf);
152         pf = nullptr;
153     }
154 }
155 
ParseJson(const std::string & bundleName,const std::string & moduleName,const std::string & jsonPath)156 void ThemeResource::ParseJson(const std::string &bundleName, const std::string &moduleName,
157     const std::string &jsonPath)
158 {
159     auto len = 0;
160     FILE* pf = std::fopen(jsonPath.c_str(), "r");
161     if (pf == nullptr) {
162         RESMGR_HILOGE(RESMGR_TAG, "fopen failed in ParseJson");
163         return;
164     }
165     std::fseek(pf, 0, SEEK_END);
166     len = ftell(pf);
167     std::fseek(pf, 0, SEEK_SET);
168     char *jsonData = (char *)malloc(len + 1);
169     if (jsonData == nullptr) {
170         RESMGR_HILOGE(RESMGR_TAG, "failed malloc in ParseJson");
171         if (pf != nullptr) {
172             fclose(pf);
173             pf = nullptr;
174         }
175         return;
176     }
177     std::fread(jsonData, len, 1, pf);
178     jsonData[len] = '\0';
179     auto themeConfig = GetThemeConfig(jsonPath);
180     cJSON *jsonValue = cJSON_Parse(jsonData);
181     if (jsonValue == nullptr) {
182         ReleaseJson(jsonData, pf);
183         RESMGR_HILOGE(RESMGR_TAG, "parse json fail");
184         return;
185     }
186     std::pair<std::string, std::string> bundleInfo(bundleName, moduleName);
187     cJSON *floatRoot = cJSON_GetObjectItem(jsonValue, "float");
188     if (floatRoot != nullptr) {
189         InitThemeRes(bundleInfo, floatRoot, themeConfig, "float");
190     }
191 
192     cJSON *colorRoot = cJSON_GetObjectItem(jsonValue, "color");
193     if (colorRoot != nullptr) {
194         InitThemeRes(bundleInfo, colorRoot, themeConfig, "color");
195     }
196     ReleaseJson(jsonData, pf);
197     cJSON_Delete(jsonValue);
198     return;
199 }
200 
ParseIcon(const std::string & bundleName,const std::string & moduleName,const std::string & iconPath)201 void ThemeResource::ParseIcon(const std::string &bundleName, const std::string &moduleName,
202     const std::string &iconPath)
203 {
204     auto themeConfig = GetThemeConfig(iconPath);
205     auto pos1 = iconPath.rfind('.');
206     auto pos2 = iconPath.rfind('/');
207     if (pos1 == std::string::npos || pos2 == std::string::npos) {
208         RESMGR_HILOGE(RESMGR_TAG, "invalid iconPath in ParseIcon");
209         return;
210     }
211     if (pos1 < pos2 + 1) {
212         return;
213     }
214     std::string iconName = iconPath.substr(pos2 + 1, pos1 - pos2 - 1);
215     auto themeValue = std::make_shared<ThemeValue>();
216     ThemeKey themeKey = ThemeKey(bundleName, moduleName, ResType::MEDIA, iconName);
217     auto themeQualifierValue = std::make_shared<ThemeQualifierValue>(themeKey, themeConfig, iconPath);
218     themeValue->AddThemeLimitPath(themeQualifierValue);
219     themeValueVec_.emplace_back(std::make_tuple(ResType::MEDIA, iconName, themeValue));
220     return;
221 }
222 
GetThemeValues(const std::pair<std::string,std::string> & bundInfo,const ResType & resType,const std::string & name)223 std::vector<std::shared_ptr<ThemeResource::ThemeValue> > ThemeResource::GetThemeValues(
224     const std::pair<std::string, std::string> &bundInfo, const ResType &resType, const std::string &name)
225 {
226     std::vector<std::shared_ptr<ThemeResource::ThemeValue> > result;
227     if (themeValueVec_.empty()) {
228         return result;
229     }
230 
231     for (const auto &themeValue : themeValueVec_) {
232         ResType type = std::get<FIRST_ELEMENT>(themeValue);
233         std::string resName = std::get<SECOND_ELEMENT>(themeValue);
234         if (type == resType && resName == name) {
235             result.emplace_back(std::get<THIRED_ELEMENT>(themeValue));
236         }
237     }
238     return result;
239 }
240 
GetFiles(const std::string & strCurrentDir)241 std::vector<std::string> GetFiles(const std::string &strCurrentDir)
242 {
243     std::vector<std::string> vFiles;
244 #if !defined(__WINNT__) && !defined(__IDE_PREVIEW__) && !defined(__ARKUI_CROSS__)
245     DIR *dir;
246     struct dirent *pDir;
247     if ((dir = opendir(strCurrentDir.c_str())) == nullptr) {
248         RESMGR_HILOGE(RESMGR_TAG, "opendir failed strCurrentDir = %{public}s", strCurrentDir.c_str());
249         return vFiles;
250     }
251     while ((pDir = readdir(dir)) != nullptr) {
252         if (strcmp(pDir->d_name, ".") == 0 || strcmp(pDir->d_name, "..") == 0) {
253             continue;
254         } else if (pDir->d_type == 8) { // 8 means the file
255             vFiles.emplace_back(strCurrentDir + "/" + pDir->d_name);
256         } else if (pDir->d_type == 4) { // 4 means the dir
257             std::string strNextDir = strCurrentDir + "/" + pDir->d_name;
258             std::vector<std::string> temp = GetFiles(strNextDir);
259             vFiles.insert(vFiles.end(), temp.begin(), temp.end());
260         } else {
261             continue;
262         }
263     }
264     closedir(dir);
265 #endif
266     return vFiles;
267 }
268 
GetBundleInfo(const std::string & rootDir,const std::string & path)269 std::tuple<std::string, std::string> GetBundleInfo(const std::string& rootDir, const std::string& path)
270 {
271     if (rootDir.empty() || path.empty()) {
272         return std::tuple<std::string, std::string>("", "");
273     }
274     size_t len = rootDir.size();
275     auto pos = rootDir.rfind('/');
276     if (pos == std::string::npos) {
277         RESMGR_HILOGE(RESMGR_TAG, "invalid rootDir = %{public}s in GetBundleInfo", rootDir.c_str());
278         return std::tuple<std::string, std::string>("", "");
279     }
280     std::string bundleName = rootDir.substr(pos + 1);
281     auto pos2 = path.find('/', len + 1);
282     if (pos2 == std::string::npos) {
283         RESMGR_HILOGE(RESMGR_TAG, "invalid path in GetBundleInfo");
284         return std::tuple<std::string, std::string>("", "");
285     }
286     if (pos2 < len + 1) {
287         return std::tuple<std::string, std::string>("", "");
288     }
289     std::string moduleName = path.substr(len + 1, pos2 - len -1);
290     std::tuple<std::string, std::string> bundleInfoTuple(bundleName, moduleName);
291     return bundleInfoTuple;
292 }
293 
LoadThemeResource(const std::string & rootDir)294 const std::shared_ptr<ThemeResource> ThemeResource::LoadThemeResource(const std::string& rootDir)
295 {
296     if (rootDir.empty()) {
297         RESMGR_HILOGE(RESMGR_TAG, "Invalid rootDir in LoadThemeResource = %{public}s", rootDir.c_str());
298         return nullptr;
299     }
300     auto themeResource = std::make_shared<ThemeResource>(rootDir);
301     std::vector<std::string> resPaths = GetFiles(rootDir);
302     for (const auto &path : resPaths) {
303         auto bundleInfo = GetBundleInfo(rootDir, path);
304         auto pos = path.rfind('.');
305         if (pos == std::string::npos) {
306             RESMGR_HILOGE(RESMGR_TAG, "invalid resPath");
307             continue;
308         }
309         std::string tail = path.substr(pos + 1);
310         if (tail == "json") {
311             themeResource->ParseJson(std::get<FIRST_ELEMENT>(bundleInfo), std::get<SECOND_ELEMENT>(bundleInfo), path);
312         } else {
313             themeResource->ParseIcon(std::get<FIRST_ELEMENT>(bundleInfo), std::get<SECOND_ELEMENT>(bundleInfo), path);
314         }
315     }
316     return themeResource;
317 }
318 
GetThemeResBundleName(const std::string & themePath)319 std::string ThemeResource::GetThemeResBundleName(const std::string &themePath)
320 {
321     auto pos = themePath.rfind('/');
322     if (pos == std::string::npos) {
323         RESMGR_HILOGE(RESMGR_TAG, "invalid themePath");
324         return std::string("");
325     }
326     std::string bundleName = themePath.substr(pos + 1);
327     return bundleName;
328 }
329 
GetIconsBundleName(const std::string & iconPath)330 std::string GetIconsBundleName(const std::string& iconPath)
331 {
332     if (iconPath.empty()) {
333         return std::string("");
334     }
335     auto pos = iconPath.rfind('/');
336     if (pos == std::string::npos) {
337         RESMGR_HILOGE(RESMGR_TAG, "invalid iconPath in GetIconsBundleName");
338         return std::string("");
339     }
340     return iconPath.substr(pos + 1);
341 }
342 
AddIconValue(const std::string & bundleName,const std::string & moduleName,const std::string & iconName,const std::string & path,const std::string & abilityName)343 void ThemeResource::AddIconValue(const std::string &bundleName, const std::string &moduleName,
344     const std::string &iconName, const std::string &path, const std::string &abilityName)
345 {
346     ThemeKey themeKey = ThemeKey(bundleName, moduleName, ResType::MEDIA, iconName, abilityName);
347     iconValues_.emplace_back(std::make_pair(themeKey, path));
348 }
349 
LoadThemeIconResource(const std::string & iconPath)350 const std::shared_ptr<ThemeResource> ThemeResource::LoadThemeIconResource(const std::string& iconPath)
351 {
352     if (iconPath.empty()) {
353         return nullptr;
354     }
355     auto themeResource = std::make_shared<ThemeResource>(iconPath);
356     std::string bundleName = GetIconsBundleName(iconPath);
357     std::vector<std::string> resPaths = GetFiles(iconPath);
358     for (const auto &path : resPaths) {
359         auto pos1 = path.rfind('.');
360         auto pos2 = path.rfind('/');
361         if (pos1 == std::string::npos || pos2 == std::string::npos || pos1 < pos2 + 1) {
362             RESMGR_HILOGE(RESMGR_TAG, "invalid path in LoadThemeIconResource");
363             continue;
364         }
365         std::string iconName = path.substr(pos2 + 1, pos1 - pos2 - 1);
366         if (path.find(DYNAMIC_ICON) != std::string::npos) {
367             auto pos3 = path.find('/', iconPath.length() + 1);
368             if (pos3 == std::string::npos || pos3 < iconPath.length() + 1) {
369                 continue;
370             }
371             std::string dynamicBundle = path.substr(iconPath.length() + 1, pos3 - iconPath.length() - 1);
372             themeResource->AddIconValue(bundleName, dynamicBundle, iconName, path);
373             continue;
374         }
375 
376         auto pos3 = path.find('/', iconPath.length() + 1);
377         if (pos3 == std::string::npos || pos3 < iconPath.length() + 1) {
378             themeResource->AddIconValue(bundleName, "", iconName, path);
379             continue;
380         }
381 
382         auto pos4 = path.find('/', pos3 + 1);
383         if (pos4 == std::string::npos || pos4 < pos3 + 1 || pos4 != pos2) {
384             themeResource->AddIconValue(bundleName, "", iconName, path);
385             continue;
386         }
387 
388         std::string abilityName = path.substr(pos3 + 1, pos4 - pos3 - 1);
389         themeResource->AddIconValue(bundleName, "", iconName, path, abilityName);
390     }
391     return themeResource;
392 }
393 
GetThemeAppIcon(const std::pair<std::string,std::string> & bundleInfo,const std::string & iconName,const std::string & abilityName)394 const std::string ThemeResource::GetThemeAppIcon(const std::pair<std::string, std::string> &bundleInfo,
395     const std::string &iconName, const std::string &abilityName)
396 {
397     std::string iconPath = GetThemeAppIconByAbilityName(bundleInfo, iconName, abilityName);
398     if (iconPath.length() != 0) {
399         return iconPath;
400     }
401 
402     for (size_t i = 0; i < iconValues_.size(); i++) {
403         if (iconValues_[i].first.bundleName == DYNAMIC_ICON && iconValues_[i].first.moduleName != bundleInfo.first) {
404             continue;
405         }
406         if (iconValues_[i].first.bundleName != DYNAMIC_ICON && iconValues_[i].first.bundleName != bundleInfo.first) {
407             continue;
408         }
409         if (iconValues_[i].first.abilityName.length() == 0 && iconName == iconValues_[i].first.resName) {
410             return iconValues_[i].second;
411         }
412     }
413     return std::string("");
414 }
415 
GetThemeAppIconByAbilityName(const std::pair<std::string,std::string> & bundleInfo,const std::string & iconName,const std::string & abilityName)416 const std::string ThemeResource::GetThemeAppIconByAbilityName(const std::pair<std::string, std::string> &bundleInfo,
417     const std::string &iconName, const std::string &abilityName)
418 {
419     if (abilityName.length() == 0) {
420         return std::string("");
421     }
422 
423     for (size_t i = 0; i < iconValues_.size(); i++) {
424         if (iconValues_[i].first.bundleName == DYNAMIC_ICON && iconValues_[i].first.moduleName != bundleInfo.first) {
425             continue;
426         }
427         if (iconValues_[i].first.bundleName != DYNAMIC_ICON && iconValues_[i].first.bundleName != bundleInfo.first) {
428             continue;
429         }
430         if (abilityName == iconValues_[i].first.abilityName && iconName == iconValues_[i].first.resName) {
431             return iconValues_[i].second;
432         }
433     }
434     return std::string("");
435 }
436 
HasIconInTheme(const std::string & bundleName)437 bool ThemeResource::HasIconInTheme(const std::string &bundleName)
438 {
439     for (size_t i = 0; i < iconValues_.size(); i++) {
440         if (iconValues_[i].first.bundleName == DYNAMIC_ICON) {
441             continue;
442         }
443 
444         if (iconValues_[i].first.moduleName == bundleName || iconValues_[i].first.bundleName == bundleName) {
445             return true;
446         }
447     }
448     return false;
449 }
450 } // namespace Resource
451 } // namespace Global
452 } // namespace OHOS
453