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