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 deviceType == DeviceType::TWO_IN_ONE) {
260 return AceType::MakeRefPtr<ResourceAdapterImpl>();
261 }
262 return RefPtr<ResourceAdapter>();
263 }
264
Init(const ResourceInfo & resourceInfo)265 void ResourceAdapterImpl::Init(const ResourceInfo& resourceInfo)
266 {
267 std::vector<std::string> hapFiles;
268 hapFiles.emplace_back(resourceInfo.GetPackagePath());
269 auto configuration = ConvertConfig(resourceInfo.GetResourceConfiguration());
270 auto handlers = resourceInfo.GetResourceHandlers();
271 bool initRet = false;
272 if (handlers.empty()) {
273 initRet = resourceManger_.InitMock(hapFiles, resourceInfo.GetSystemPackagePath(), configuration);
274 } else {
275 initRet = resourceManger_.Init(hapFiles, handlers);
276 resourceManger_.UpdateConfig(configuration);
277 }
278 LOGI("Init result=%{public}d, handle=%{public}zu, ori=%{public}d, dpi=%{public}f, device=%{public}d, "
279 "font=%{public}f, color=%{public}d",
280 initRet, handlers.size(), configuration.orientation_, configuration.resolution_, configuration.deviceType_,
281 configuration.fontRatio_, configuration.colorMode_);
282 }
283
UpdateConfig(const ResourceConfiguration & config)284 void ResourceAdapterImpl::UpdateConfig(const ResourceConfiguration& config)
285 {
286 auto configuration = ConvertConfig(config);
287 LOGI("UpdateConfig ori=%{public}d, dpi=%{public}f, device=%{public}d, font=%{public}f, color=%{public}d",
288 configuration.orientation_, configuration.resolution_, configuration.deviceType_, configuration.fontRatio_,
289 configuration.colorMode_);
290 resourceManger_.UpdateConfig(configuration);
291 }
292
GetTheme(int32_t themeId)293 RefPtr<ThemeStyle> ResourceAdapterImpl::GetTheme(int32_t themeId)
294 {
295 CheckThemeId(themeId);
296 auto theme = AceType::MakeRefPtr<RawThemeStyle>(AceType::Claim(this));
297 auto& attrMap = theme->rawAttrs_;
298 auto ret = resourceManger_.GetTheme(themeId, attrMap);
299 LOGI("GetTheme themeId=%{public}d, ret=%{public}d, attr size=%{public}zu", themeId, ret, attrMap.size());
300 auto iter = attrMap.find(THEME_ATTR_BG_COLOR);
301 if (iter != attrMap.end()) {
302 auto& attribute = iter->second;
303 if (attribute) {
304 Color bgColor(attribute->GetColorValue());
305 theme->SetAttr(THEME_ATTR_BG_COLOR, { .type = ThemeConstantsType::COLOR, .value = bgColor });
306 }
307 }
308 return theme;
309 }
310
GetColor(uint32_t resId)311 Color ResourceAdapterImpl::GetColor(uint32_t resId)
312 {
313 uint32_t result = 0;
314 auto ret = resourceManger_.GetColor(static_cast<int32_t>(resId), result);
315 if (!ret) {
316 LOGE("GetColor error, id=%{public}u", resId);
317 }
318 return Color(result);
319 }
320
GetDimension(uint32_t resId)321 Dimension ResourceAdapterImpl::GetDimension(uint32_t resId)
322 {
323 float result = 0;
324 std::string unit = "";
325 auto ret = resourceManger_.GetFloat(static_cast<int32_t>(resId), result, unit);
326 if (!ret) {
327 LOGE("GetDimension error, id=%{public}u", resId);
328 }
329 return Dimension(result, ParseDimensionUnit(unit));
330 }
331
GetString(uint32_t resId)332 std::string ResourceAdapterImpl::GetString(uint32_t resId)
333 {
334 std::string strResult = "";
335 auto ret = resourceManger_.GetString(static_cast<int32_t>(resId), strResult);
336 if (!ret) {
337 LOGE("GetString error, id=%{public}u", resId);
338 }
339 return strResult;
340 }
341
GetPluralString(uint32_t resId,int quantity)342 std::string ResourceAdapterImpl::GetPluralString(uint32_t resId, int quantity)
343 {
344 std::vector<std::string> pluralResults;
345 auto ret = resourceManger_.GetStringArray(static_cast<int32_t>(resId), pluralResults);
346 if (!ret) {
347 LOGE("GetPluralString error, id=%{public}u", resId);
348 }
349
350 auto pluralChoice = Localization::GetInstance()->PluralRulesFormat(quantity);
351 auto iter = std::find(pluralResults.begin(), pluralResults.end(), pluralChoice);
352 std::string originStr;
353 if (iter != pluralResults.end() && ++iter != pluralResults.end()) {
354 originStr = *iter;
355 }
356 return originStr;
357 }
358
GetStringArray(uint32_t resId) const359 std::vector<std::string> ResourceAdapterImpl::GetStringArray(uint32_t resId) const
360 {
361 std::vector<std::string> strResults;
362 auto ret = resourceManger_.GetStringArray(static_cast<int32_t>(resId), strResults);
363 if (!ret) {
364 LOGE("GetStringArray error, id=%{public}u", resId);
365 }
366 return strResults;
367 }
368
GetDouble(uint32_t resId)369 double ResourceAdapterImpl::GetDouble(uint32_t resId)
370 {
371 float result = 0.0f;
372 auto ret = resourceManger_.GetFloat(static_cast<int32_t>(resId), result);
373 if (!ret) {
374 LOGE("GetDouble error, id=%{public}u", resId);
375 }
376 return static_cast<double>(result);
377 }
378
GetInt(uint32_t resId)379 int32_t ResourceAdapterImpl::GetInt(uint32_t resId)
380 {
381 int32_t result = 0;
382 auto ret = resourceManger_.GetInt(static_cast<int32_t>(resId), result);
383 if (!ret) {
384 LOGE("GetInt error, id=%{public}u", resId);
385 }
386 return result;
387 }
388
GetResource(uint32_t resId,std::ostream & dest) const389 bool ResourceAdapterImpl::GetResource(uint32_t resId, std::ostream& dest) const
390 {
391 return resourceManger_.GetResource(static_cast<int32_t>(resId), dest);
392 }
393
GetResource(const std::string & path,std::ostream & dest) const394 bool ResourceAdapterImpl::GetResource(const std::string& path, std::ostream& dest) const
395 {
396 return resourceManger_.GetResource(path, dest);
397 }
398
ConvertToGlobalResourceType(const std::string & resTypeName,Global::Resource::ResourceType & resType) const399 bool ResourceAdapterImpl::ConvertToGlobalResourceType(
400 const std::string& resTypeName, Global::Resource::ResourceType& resType) const
401 {
402 if (resTypeName == "color") {
403 resType = Global::Resource::ResourceType::COLOR;
404 return true;
405 }
406 if (resTypeName == "float") {
407 resType = Global::Resource::ResourceType::FLOAT;
408 return true;
409 }
410 if (resTypeName == "string") {
411 resType = Global::Resource::ResourceType::STRING;
412 return true;
413 }
414 if (resTypeName == "media") {
415 resType = Global::Resource::ResourceType::MEDIA;
416 return true;
417 }
418 LOGE("unsupported resource type(=%{public}s)", resTypeName.c_str());
419 return false;
420 }
421
GetIdByName(const std::string & resName,const std::string & resType,uint32_t & resId) const422 bool ResourceAdapterImpl::GetIdByName(const std::string& resName, const std::string& resType, uint32_t& resId) const
423 {
424 Global::Resource::ResourceType globalResType;
425 if (!ConvertToGlobalResourceType(resType, globalResType)) {
426 return false;
427 }
428 int32_t globalResId = 0;
429 if (!resourceManger_.GetIdByName("", resName, globalResType, globalResId)) {
430 LOGE("get resource id failed.(name=%{public}s, type=%{public}s)", resName.c_str(), resType.c_str());
431 return false;
432 }
433 resId = static_cast<uint32_t>(globalResId);
434 return true;
435 }
436
GetMediaPath(uint32_t resId)437 std::string ResourceAdapterImpl::GetMediaPath(uint32_t resId)
438 {
439 std::string mediaPath = "";
440 auto ret = resourceManger_.GetString(static_cast<int32_t>(resId), mediaPath);
441 if (!ret) {
442 LOGE("GetMediaPath error, id=%{public}u", resId);
443 return "";
444 }
445 return "resource://" + mediaPath.substr(0, mediaPath.find_last_of("/")) + "/" +
446 std::to_string(resId) + mediaPath.substr(mediaPath.find_last_of("."));
447 }
448
GetRawfile(const std::string & fileName)449 std::string ResourceAdapterImpl::GetRawfile(const std::string& fileName)
450 {
451 auto container = Container::Current();
452 if (!container) {
453 LOGW("container is null");
454 return "";
455 }
456 auto moduleName = container->GetModuleName();
457 #if defined(PREVIEW)
458 return "resource://RAWFILE/" + moduleName + "/resources/rawfile/" + fileName;
459 #else
460 return "resource://RAWFILE/assets/" + moduleName + "/resources/rawfile/" + fileName;
461 #endif
462 }
463 } // namespace OHOS::Ace
464