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