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