1 /*
2 * Copyright (c) 2021-2022 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 "adapter/preview/osal/resource_adapter_impl.h"
17
18 #include <set>
19
20 #include "base/i18n/localization.h"
21 #include "base/utils/system_properties.h"
22 #include "core/common/ace_application_info.h"
23 #include "core/common/container.h"
24 #include "core/components/common/layout/constants.h"
25 #include "core/components/theme/theme_attributes.h"
26
27 namespace OHOS::Ace {
28 namespace {
29
30 constexpr char COLOR_VALUE_PREFIX[] = "$color:";
31 constexpr char PATTERN_NAME_KEY_WORD[] = "$pattern:";
32 constexpr char STATE_VALUE_KEY_WORD[] = ".sxml";
33 constexpr char REF_ATTR_VALUE_KEY_WORD[] = "?theme:";
34 constexpr char STATE_CONTAINER[] = "state-container";
35 constexpr char STATE_ELEMENT[] = "element";
36 constexpr uint32_t STATE_MAX = 128;
37 constexpr double DPI_BASE = 160.0;
38 constexpr uint32_t THEME_ID_LIGHT = 117440515;
39 constexpr uint32_t THEME_ID_DARK = 117440516;
40
CheckThemeId(int32_t & themeId)41 void CheckThemeId(int32_t& themeId)
42 {
43 if (themeId >= 0) {
44 return;
45 }
46 auto deviceType = SystemProperties::GetDeviceType();
47 themeId = (deviceType == DeviceType::PHONE || deviceType == DeviceType::UNKNOWN || deviceType == DeviceType::CAR)
48 ? THEME_ID_LIGHT
49 : THEME_ID_DARK;
50 }
51
ParseDimensionUnit(const std::string & unit)52 DimensionUnit ParseDimensionUnit(const std::string& unit)
53 {
54 if (unit == "px") {
55 return DimensionUnit::PX;
56 } else if (unit == "fp") {
57 return DimensionUnit::FP;
58 } else if (unit == "lpx") {
59 return DimensionUnit::LPX;
60 } else if (unit == "%") {
61 return DimensionUnit::PERCENT;
62 } else {
63 return DimensionUnit::VP;
64 }
65 };
66
ConvertOrientation(DeviceOrientation orientation)67 Global::Resource::ORIENTATION ConvertOrientation(DeviceOrientation orientation)
68 {
69 return orientation == DeviceOrientation::PORTRAIT ? Global::Resource::ORIENTATION::ORIENTATION_PORTRAIT
70 : Global::Resource::ORIENTATION::ORIENTATION_LANDSCAPE;
71 }
72
ConvertResolution(double density)73 Global::Resource::RESOLUTION ConvertResolution(double density)
74 {
75 static const std::vector<std::pair<double, Global::Resource::RESOLUTION>> resolutions = {
76 { 120.0, Global::Resource::RESOLUTION::RESOLUTION_LOW },
77 { 160.0, Global::Resource::RESOLUTION::RESOLUTION_MEDIUM },
78 { 240.0, Global::Resource::RESOLUTION::RESOLUTION_HIGH },
79 { 320.0, Global::Resource::RESOLUTION::RESOLUTION_XHIGH },
80 { 480.0, Global::Resource::RESOLUTION::RESOLUTION_XXHIGH },
81 { 640.0, Global::Resource::RESOLUTION::RESOLUTION_XXXHIGH },
82 };
83 double deviceDpi = density * DPI_BASE;
84 auto resolution = Global::Resource::RESOLUTION::RESOLUTION_LOW;
85 for (const auto& [dpi, value] : resolutions) {
86 resolution = value;
87 if (LessOrEqual(deviceDpi, dpi)) {
88 break;
89 }
90 }
91 return resolution;
92 }
93
ConvertDeviceType(DeviceType type)94 Global::Resource::DEVICE_TYPE ConvertDeviceType(DeviceType type)
95 {
96 switch (type) {
97 case DeviceType::PHONE:
98 return Global::Resource::DEVICE_TYPE::DEVICE_TYPE_PHONE;
99 case DeviceType::TV:
100 return Global::Resource::DEVICE_TYPE::DEVICE_TYPE_TV;
101 case DeviceType::WATCH:
102 return Global::Resource::DEVICE_TYPE::DEVICE_TYPE_WATCH;
103 case DeviceType::CAR:
104 return Global::Resource::DEVICE_TYPE::DEVICE_TYPE_CAR;
105 case DeviceType::TABLET:
106 return Global::Resource::DEVICE_TYPE::DEVICE_TYPE_TABLET;
107 default:
108 return Global::Resource::DEVICE_TYPE::DEVICE_TYPE_UNDEFINED;
109 }
110 }
111
ConvertColorMode(ColorMode colorMode)112 Global::Resource::COLOR_MODE ConvertColorMode(ColorMode colorMode)
113 {
114 return colorMode == ColorMode::LIGHT ? Global::Resource::COLOR_MODE::COLOR_MODE_LIGHT
115 : Global::Resource::COLOR_MODE::COLOR_MODE_DARK;
116 }
117
ConvertConfig(const ResourceConfiguration & config)118 Global::Resource::Configuration ConvertConfig(const ResourceConfiguration& config)
119 {
120 Global::Resource::Configuration::Locale locale(AceApplicationInfo::GetInstance().GetLanguage(),
121 AceApplicationInfo::GetInstance().GetCountryOrRegion(), AceApplicationInfo::GetInstance().GetScript());
122 Global::Resource::Configuration globalConfig = {
123 .locales_ = { locale },
124 .orientation_ = ConvertOrientation(config.GetOrientation()),
125 .resolution_ = ConvertResolution(config.GetDensity()),
126 .deviceType_ = ConvertDeviceType(config.GetDeviceType()),
127 .fontRatio_ = config.GetFontRatio(),
128 .colorMode_ = ConvertColorMode(config.GetColorMode()),
129 };
130 return globalConfig;
131 }
132
ParseStateResource(const std::string & styleName,const std::string & attrName,std::unique_ptr<Global::Resource::SolidXmlWrapper> xmlWrapper)133 RefPtr<StateResource> ParseStateResource(const std::string& styleName, const std::string& attrName,
134 std::unique_ptr<Global::Resource::SolidXmlWrapper> xmlWrapper)
135 {
136 auto rootNode = xmlWrapper->GetRoot();
137 if (!rootNode) {
138 LOGE("Parse %{public}s state resource %{public}s error! No root!", styleName.c_str(), attrName.c_str());
139 return nullptr;
140 }
141 if (rootNode->GetNodeName() != STATE_CONTAINER) {
142 return nullptr;
143 }
144 auto stateResource = AceType::MakeRefPtr<StateResource>();
145 auto node = rootNode->GetChild();
146 uint32_t stateCount = 0;
147 while (node && ++stateCount < STATE_MAX) {
148 // Parse each item
149 auto nodeAttrs = node->GetAttributes();
150 auto valueFindIter = nodeAttrs.find(STATE_ELEMENT);
151 if (valueFindIter == nodeAttrs.end()) {
152 continue;
153 }
154 auto stateColor = Color(valueFindIter->second.GetColorValue());
155 uint32_t state = STATE_NORMAL;
156 static const std::unordered_map<std::string, uint32_t> stateMap = {
157 { "state_pressed", STATE_PRESSED },
158 { "state_focus", STATE_FOCUS },
159 { "state_checked", STATE_CHECKED },
160 { "state_disabled", STATE_DISABLED },
161 { "state_waiting", STATE_WAITING },
162 { "state_hovered", STATE_HOVERED },
163 };
164 for (auto& [stateKey, stateValue] : nodeAttrs) {
165 auto stateFindIter = stateMap.find(stateKey);
166 if (stateFindIter == stateMap.end()) {
167 continue;
168 }
169 if (stateValue.GetStringValue() != "true") {
170 continue;
171 }
172 state |= stateFindIter->second;
173 }
174 stateResource->SetStateValue(state, { .type = ThemeConstantsType::COLOR, .value = stateColor });
175 node = node->GetSibling();
176 }
177 return stateResource;
178 }
179 } // namespace
180
181 class RawThemeStyle : public ThemeStyle {
182 DECLARE_ACE_TYPE(RawThemeStyle, ThemeStyle);
183
184 public:
185 friend class ResourceAdapterImpl;
186 using RawAttrMap = std::unordered_map<std::string, std::unique_ptr<Global::Resource::TypeAttribute>>;
187
RawThemeStyle(RefPtr<ResourceAdapter> resAdapter)188 explicit RawThemeStyle(RefPtr<ResourceAdapter> resAdapter) : resAdapter_(resAdapter) {}
189 ~RawThemeStyle() override = default;
190
191 void ParseContent() override;
192
193 private:
194 RawAttrMap rawAttrs_; // key and value read from global resource api.
195 RefPtr<ResourceAdapter> resAdapter_;
196 };
197
ParseContent()198 void RawThemeStyle::ParseContent()
199 {
200 static const std::set<std::string> stringAttrs = {
201 "attr_text_font_family_regular",
202 "attr_text_font_family_medium"
203 };
204 for (auto& [attrName, attrValue] : rawAttrs_) {
205 if (!attrValue) {
206 continue;
207 }
208 auto rawString = attrValue->GetOriginalValue();
209 if (rawString.size() == 0) {
210 continue;
211 }
212 if (rawString.front() == '#' || rawString.find(COLOR_VALUE_PREFIX) != std::string::npos) {
213 // color
214 attributes_[attrName] = { .type = ThemeConstantsType::COLOR, .value = Color(attrValue->GetColorValue()) };
215 } else if (stringAttrs.find(attrName) != stringAttrs.end()) {
216 // string
217 attributes_[attrName] = { .type = ThemeConstantsType::STRING, .value = rawString };
218 } else if (rawString.find(PATTERN_NAME_KEY_WORD) != std::string::npos) {
219 // pattern
220 auto patternStyle = AceType::MakeRefPtr<RawThemeStyle>(resAdapter_);
221 patternStyle->SetName(attrName);
222 patternStyle->parentStyle_ = AceType::WeakClaim(this);
223 patternStyle->rawAttrs_ = attrValue->GetPattern();
224 patternStyle->ParseContent();
225 attributes_[attrName] = { .type = ThemeConstantsType::PATTERN,
226 .value = RefPtr<ThemeStyle>(std::move(patternStyle)) };
227 } else if (rawString.rfind(STATE_VALUE_KEY_WORD) != std::string::npos) {
228 // state graphic value
229 auto xmlWrapper = attrValue->GetLayoutValue();
230 if (!xmlWrapper) {
231 LOGE("Parse %{public}s state resource %{public}s error! xml is null!", name_.c_str(), attrName.c_str());
232 continue;
233 }
234 auto stateResource = ParseStateResource(name_, attrName, std::move(xmlWrapper));
235 if (!stateResource) {
236 continue;
237 }
238 attributes_[attrName] = { .type = ThemeConstantsType::STATE_RESOURCE, .value = stateResource };
239 } else if (rawString.find(REF_ATTR_VALUE_KEY_WORD) != std::string::npos) {
240 attributes_[attrName] = { .type = ThemeConstantsType::REFERENCE_ATTR, .value = rawString };
241 } else {
242 // double & dimension
243 std::string unit = "";
244 auto doubleValue = static_cast<double>(attrValue->GetFloat(unit));
245 if (unit.empty()) {
246 attributes_[attrName] = { .type = ThemeConstantsType::DOUBLE, .value = doubleValue };
247 } else {
248 attributes_[attrName] = { .type = ThemeConstantsType::DIMENSION,
249 .value = Dimension(doubleValue, ParseDimensionUnit(unit)) };
250 }
251 }
252 }
253 }
254
Create()255 RefPtr<ResourceAdapter> ResourceAdapter::Create()
256 {
257 auto deviceType = SystemProperties::GetDeviceType();
258 if (deviceType == DeviceType::PHONE || deviceType == DeviceType::CAR || deviceType == DeviceType::TABLET) {
259 return AceType::MakeRefPtr<ResourceAdapterImpl>();
260 }
261 return RefPtr<ResourceAdapter>();
262 }
263
Init(const ResourceInfo & resourceInfo)264 void ResourceAdapterImpl::Init(const ResourceInfo& resourceInfo)
265 {
266 std::vector<std::string> hapFiles;
267 hapFiles.emplace_back(resourceInfo.GetPackagePath());
268 auto configuration = ConvertConfig(resourceInfo.GetResourceConfiguration());
269 auto handlers = resourceInfo.GetResourceHandlers();
270 bool initRet = false;
271 if (handlers.empty()) {
272 initRet = resourceManger_.InitMock(hapFiles, resourceInfo.GetSystemPackagePath(), configuration);
273 } else {
274 initRet = resourceManger_.Init(hapFiles, handlers);
275 resourceManger_.UpdateConfig(configuration);
276 }
277 LOGI("Init result=%{public}d, handle=%{public}zu, ori=%{public}d, dpi=%{public}f, device=%{public}d, "
278 "font=%{public}f, color=%{public}d",
279 initRet, handlers.size(), configuration.orientation_, configuration.resolution_, configuration.deviceType_,
280 configuration.fontRatio_, configuration.colorMode_);
281 }
282
Reload()283 void ResourceAdapterImpl::Reload() {}
284
UpdateConfig(const ResourceConfiguration & config)285 void ResourceAdapterImpl::UpdateConfig(const ResourceConfiguration& config)
286 {
287 auto configuration = ConvertConfig(config);
288 LOGI("UpdateConfig ori=%{public}d, dpi=%{public}f, device=%{public}d, font=%{public}f, color=%{public}d",
289 configuration.orientation_, configuration.resolution_, configuration.deviceType_, configuration.fontRatio_,
290 configuration.colorMode_);
291 resourceManger_.UpdateConfig(configuration);
292 }
293
GetTheme(int32_t themeId)294 RefPtr<ThemeStyle> ResourceAdapterImpl::GetTheme(int32_t themeId)
295 {
296 CheckThemeId(themeId);
297 auto theme = AceType::MakeRefPtr<RawThemeStyle>(AceType::Claim(this));
298 auto& attrMap = theme->rawAttrs_;
299 auto ret = resourceManger_.GetTheme(themeId, attrMap);
300 LOGI("GetTheme themeId=%{public}d, ret=%{public}d, attr size=%{public}zu", themeId, ret, attrMap.size());
301 auto iter = attrMap.find(THEME_ATTR_BG_COLOR);
302 if (iter != attrMap.end()) {
303 auto& attribute = iter->second;
304 if (attribute) {
305 Color bgColor(attribute->GetColorValue());
306 theme->SetAttr(THEME_ATTR_BG_COLOR, { .type = ThemeConstantsType::COLOR, .value = bgColor });
307 }
308 }
309 return theme;
310 }
311
GetColor(uint32_t resId)312 Color ResourceAdapterImpl::GetColor(uint32_t resId)
313 {
314 uint32_t result = 0;
315 auto ret = resourceManger_.GetColor(static_cast<int32_t>(resId), result);
316 if (!ret) {
317 LOGE("GetColor error, id=%{public}u", resId);
318 }
319 return Color(result);
320 }
321
GetDimension(uint32_t resId)322 Dimension ResourceAdapterImpl::GetDimension(uint32_t resId)
323 {
324 float result = 0;
325 std::string unit = "";
326 auto ret = resourceManger_.GetFloat(static_cast<int32_t>(resId), result, unit);
327 if (!ret) {
328 LOGE("GetDimension error, id=%{public}u", resId);
329 }
330 return Dimension(result, ParseDimensionUnit(unit));
331 }
332
GetString(uint32_t resId)333 std::string ResourceAdapterImpl::GetString(uint32_t resId)
334 {
335 std::string strResult = "";
336 auto ret = resourceManger_.GetString(static_cast<int32_t>(resId), strResult);
337 if (!ret) {
338 LOGE("GetString error, id=%{public}u", resId);
339 }
340 return strResult;
341 }
342
GetPluralString(uint32_t resId,int quantity)343 std::string ResourceAdapterImpl::GetPluralString(uint32_t resId, int quantity)
344 {
345 std::vector<std::string> pluralResults;
346 auto ret = resourceManger_.GetStringArray(static_cast<int32_t>(resId), pluralResults);
347 if (!ret) {
348 LOGE("GetPluralString error, id=%{public}u", resId);
349 }
350
351 auto pluralChoice = Localization::GetInstance()->PluralRulesFormat(quantity);
352 auto iter = std::find(pluralResults.begin(), pluralResults.end(), pluralChoice);
353 std::string originStr;
354 if (iter != pluralResults.end() && ++iter != pluralResults.end()) {
355 originStr = *iter;
356 }
357 return originStr;
358 }
359
GetStringArray(uint32_t resId) const360 std::vector<std::string> ResourceAdapterImpl::GetStringArray(uint32_t resId) const
361 {
362 std::vector<std::string> strResults;
363 auto ret = resourceManger_.GetStringArray(static_cast<int32_t>(resId), strResults);
364 if (!ret) {
365 LOGE("GetStringArray error, id=%{public}u", resId);
366 }
367 return strResults;
368 }
369
GetDouble(uint32_t resId)370 double ResourceAdapterImpl::GetDouble(uint32_t resId)
371 {
372 float result = 0.0f;
373 auto ret = resourceManger_.GetFloat(static_cast<int32_t>(resId), result);
374 if (!ret) {
375 LOGE("GetDouble error, id=%{public}u", resId);
376 }
377 return static_cast<double>(result);
378 }
379
GetInt(uint32_t resId)380 int32_t ResourceAdapterImpl::GetInt(uint32_t resId)
381 {
382 int32_t result = 0;
383 auto ret = resourceManger_.GetInt(static_cast<int32_t>(resId), result);
384 if (!ret) {
385 LOGE("GetInt error, id=%{public}u", resId);
386 }
387 return result;
388 }
389
GetResource(uint32_t resId,std::ostream & dest) const390 bool ResourceAdapterImpl::GetResource(uint32_t resId, std::ostream& dest) const
391 {
392 return resourceManger_.GetResource(static_cast<int32_t>(resId), dest);
393 }
394
GetResource(const std::string & path,std::ostream & dest) const395 bool ResourceAdapterImpl::GetResource(const std::string& path, std::ostream& dest) const
396 {
397 return resourceManger_.GetResource(path, dest);
398 }
399
ConvertToGlobalResourceType(const std::string & resTypeName,Global::Resource::ResourceType & resType) const400 bool ResourceAdapterImpl::ConvertToGlobalResourceType(
401 const std::string& resTypeName, Global::Resource::ResourceType& resType) const
402 {
403 if (resTypeName == "color") {
404 resType = Global::Resource::ResourceType::COLOR;
405 return true;
406 }
407 if (resTypeName == "float") {
408 resType = Global::Resource::ResourceType::FLOAT;
409 return true;
410 }
411 if (resTypeName == "string") {
412 resType = Global::Resource::ResourceType::STRING;
413 return true;
414 }
415 if (resTypeName == "media") {
416 resType = Global::Resource::ResourceType::MEDIA;
417 return true;
418 }
419 LOGE("unsupported resource type(=%{public}s)", resTypeName.c_str());
420 return false;
421 }
422
GetIdByName(const std::string & resName,const std::string & resType,uint32_t & resId) const423 bool ResourceAdapterImpl::GetIdByName(const std::string& resName, const std::string& resType, uint32_t& resId) const
424 {
425 Global::Resource::ResourceType globalResType;
426 if (!ConvertToGlobalResourceType(resType, globalResType)) {
427 return false;
428 }
429 int32_t globalResId = 0;
430 if (!resourceManger_.GetIdByName("", resName, globalResType, globalResId)) {
431 LOGE("get resource id failed.(name=%{public}s, type=%{public}s)", resName.c_str(), resType.c_str());
432 return false;
433 }
434 resId = static_cast<uint32_t>(globalResId);
435 return true;
436 }
437
GetMediaPath(uint32_t resId)438 std::string ResourceAdapterImpl::GetMediaPath(uint32_t resId)
439 {
440 std::string mediaPath = "";
441 auto ret = resourceManger_.GetString(static_cast<int32_t>(resId), mediaPath);
442 if (!ret) {
443 LOGE("GetMediaPath error, id=%{public}u", resId);
444 return "";
445 }
446 return "resource://" + mediaPath.substr(0, mediaPath.find_last_of("/")) + "/" +
447 std::to_string(resId) + mediaPath.substr(mediaPath.find_last_of("."));
448 }
449
GetRawfile(const std::string & fileName)450 std::string ResourceAdapterImpl::GetRawfile(const std::string& fileName)
451 {
452 auto container = Container::Current();
453 if (!container) {
454 LOGW("container is null");
455 return "";
456 }
457 auto moduleName = container->GetModuleName();
458 #if defined(PREVIEW)
459 return "resource://RAWFILE/" + moduleName + "/resources/rawfile/" + fileName;
460 #else
461 return "resource://RAWFILE/assets/" + moduleName + "/resources/rawfile/" + fileName;
462 #endif
463 }
464 } // namespace OHOS::Ace
465