1 /*
2  * Copyright (c) 2021-2024 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 "core/components/theme/theme_constants.h"
17 
18 #include "base/resource/ace_res_config.h"
19 
20 namespace OHOS::Ace {
21 namespace {
22 
23 const ResValueWrapper ERROR_VALUE = { .type = ThemeConstantsType::ERROR };
24 // Don't use Color::BLACK in case Color haven't been initialized.
25 const Color ERROR_VALUE_COLOR = Color(0xff000000);
26 constexpr Dimension ERROR_VALUE_DIMENSION = 0.0_vp;
27 constexpr int32_t ERROR_VALUE_INT = 0;
28 constexpr uint32_t ERROR_VALUE_UINT = 0;
29 constexpr double ERROR_VALUE_DOUBLE = 0.0;
30 constexpr double BLEND_ALPHA_MAX = 1.0;
31 constexpr InternalResource::ResourceId ERROR_VALUE_RESOURCE_ID = InternalResource::ResourceId::NO_ID;
32 const char STYLES_FOLDER_PATH[] = "resources/styles/";
33 const char FILE_TYPE_JSON[] = ".json";
34 const char CUSTOM_STYLE_ROOT_NAME[] = "style";
35 const Color TRANSPARENT_BG_COLOR = Color::FromRGBO(0, 0, 0, 0.2);
36 // For global resource manager system, system resource id is in [0x7000000, 0x7ffffff],
37 // and the id of resource defined by developer in the "resource" directory is greater than or equal to 0x1000000.
38 constexpr uint32_t GLOBAL_RESOURCE_ID_START = 0x1000000;
39 
40 DeviceType g_deviceType = DeviceType::PHONE;
41 
42 // Check whether value is match with expected type
ValueTypeMatch(const ResValueWrapper & valueWrapper,uint32_t key,const ThemeConstantsType & expectType)43 bool ValueTypeMatch(const ResValueWrapper& valueWrapper, uint32_t key, const ThemeConstantsType& expectType)
44 {
45     if (valueWrapper.type == ThemeConstantsType::ERROR) {
46         return false;
47     }
48     if (valueWrapper.type != expectType) {
49         return false;
50     }
51     return true;
52 }
53 
IsGlobalResource(uint32_t resId)54 bool IsGlobalResource(uint32_t resId)
55 {
56     return resId >= GLOBAL_RESOURCE_ID_START;
57 }
58 
59 } // namespace
60 
InitDeviceType()61 void ThemeConstants::InitDeviceType()
62 {
63     g_deviceType = SystemProperties::GetDeviceType();
64 }
65 
GetPlatformConstants(uint32_t key)66 const ResValueWrapper* ThemeConstants::GetPlatformConstants(uint32_t key)
67 {
68 #ifdef WEARABLE_PRODUCT
69     if (g_deviceType == DeviceType::WATCH && key < ThemeConstants::WatchMapCount &&
70         ThemeConstants::styleMapWatch[key] != nullptr) {
71         return ThemeConstants::styleMapWatch[key];
72     }
73 #else
74     if (g_deviceType == DeviceType::TV && key < ThemeConstants::TvMapCount &&
75         ThemeConstants::styleMapTv[key] != nullptr) {
76         return ThemeConstants::styleMapTv[key];
77     }
78 #endif
79     if (key < ThemeConstants::DefaultMapCount) {
80         return ThemeConstants::styleMapDefault[key];
81     }
82     return nullptr;
83 }
84 
GetColor(uint32_t key) const85 Color ThemeConstants::GetColor(uint32_t key) const
86 {
87     if (IsGlobalResource(key)) {
88         if (!resAdapter_) {
89             return ERROR_VALUE_COLOR;
90         }
91         return resAdapter_->GetColor(key);
92     }
93     const auto& valueWrapper = GetValue(key);
94     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::COLOR)) {
95         return ERROR_VALUE_COLOR;
96     }
97     auto colorPair = valueWrapper.GetValue<Color>(ERROR_VALUE_COLOR);
98     if (!colorPair.first) {
99         TAG_LOGW(AceLogTag::ACE_THEME, "Get theme color error: %{public}u, type: %{public}u", key, valueWrapper.type);
100     }
101     return colorPair.second;
102 }
103 
GetColorByName(const std::string & resName) const104 Color ThemeConstants::GetColorByName(const std::string& resName) const
105 {
106     if (!resAdapter_) {
107         return ERROR_VALUE_COLOR;
108     }
109     return resAdapter_->GetColorByName(resName);
110 }
111 
GetDimension(uint32_t key) const112 Dimension ThemeConstants::GetDimension(uint32_t key) const
113 {
114     if (IsGlobalResource(key)) {
115         if (!resAdapter_) {
116             return ERROR_VALUE_DIMENSION;
117         }
118         auto result = resAdapter_->GetDimension(key);
119         if (NearZero(result.Value())) {
120             result = StringUtils::StringToDimension(resAdapter_->GetString(key));
121         }
122         return result;
123     }
124     const auto& valueWrapper = GetValue(key);
125     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::DIMENSION)) {
126         return ERROR_VALUE_DIMENSION;
127     }
128     auto dimensionPair = valueWrapper.GetValue<Dimension>(ERROR_VALUE_DIMENSION);
129     if (!dimensionPair.first) {
130         TAG_LOGW(
131             AceLogTag::ACE_THEME, "Get theme dimension error: %{public}u, type: %{public}u", key, valueWrapper.type);
132     }
133     return dimensionPair.second;
134 }
135 
GetDimensionByName(const std::string & resName) const136 Dimension ThemeConstants::GetDimensionByName(const std::string& resName) const
137 {
138     if (!resAdapter_) {
139         return ERROR_VALUE_DIMENSION;
140     }
141     auto result = resAdapter_->GetDimensionByName(resName);
142     if (NearZero(result.Value())) {
143         result = StringUtils::StringToDimension(resAdapter_->GetStringByName(resName));
144     }
145     return result;
146 }
147 
GetInt(uint32_t key) const148 int32_t ThemeConstants::GetInt(uint32_t key) const
149 {
150     if (IsGlobalResource(key)) {
151         if (!resAdapter_) {
152             return ERROR_VALUE_INT;
153         }
154         return resAdapter_->GetInt(key);
155     }
156     const auto& valueWrapper = GetValue(key);
157     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::INT)) {
158         return ERROR_VALUE_INT;
159     }
160     auto intPair = valueWrapper.GetValue<int32_t>(ERROR_VALUE_INT);
161     if (!intPair.first) {
162         TAG_LOGW(AceLogTag::ACE_THEME, "Get theme int error: %{public}u, type: %{public}u", key, valueWrapper.type);
163     }
164     return intPair.second;
165 }
166 
GetIntByName(const std::string & resName) const167 int32_t ThemeConstants::GetIntByName(const std::string& resName) const
168 {
169     if (!resAdapter_) {
170         return ERROR_VALUE_INT;
171     }
172     return resAdapter_->GetIntByName(resName);
173 }
174 
GetDouble(uint32_t key) const175 double ThemeConstants::GetDouble(uint32_t key) const
176 {
177     if (IsGlobalResource(key)) {
178         if (!resAdapter_) {
179             return ERROR_VALUE_DOUBLE;
180         }
181         return resAdapter_->GetDouble(key);
182     }
183     const auto& valueWrapper = GetValue(key);
184     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::DOUBLE)) {
185         return ERROR_VALUE_DOUBLE;
186     }
187     auto doublePair = valueWrapper.GetValue<double>(ERROR_VALUE_DOUBLE);
188     if (!doublePair.first) {
189         TAG_LOGW(AceLogTag::ACE_THEME, "Get theme double error: %{public}u, type: %{public}u", key, valueWrapper.type);
190     }
191     return doublePair.second;
192 }
193 
GetDoubleByName(const std::string & resName) const194 double ThemeConstants::GetDoubleByName(const std::string& resName) const
195 {
196     if (!resAdapter_) {
197         return ERROR_VALUE_DOUBLE;
198     }
199     return resAdapter_->GetDoubleByName(resName);
200 }
201 
GetString(uint32_t key) const202 std::string ThemeConstants::GetString(uint32_t key) const
203 {
204     if (IsGlobalResource(key)) {
205         if (!resAdapter_) {
206             return "";
207         }
208         return resAdapter_->GetString(key);
209     }
210     const auto& valueWrapper = GetValue(key);
211     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::STRING)) {
212         return "";
213     }
214     auto stringPair = valueWrapper.GetValue<std::string>("");
215     if (!stringPair.first) {
216         TAG_LOGW(AceLogTag::ACE_THEME, "Get theme string error: %{public}u, type: %{public}u", key, valueWrapper.type);
217     }
218     return stringPair.second;
219 }
220 
GetStringByName(const std::string & resName) const221 std::string ThemeConstants::GetStringByName(const std::string& resName) const
222 {
223     if (!resAdapter_) {
224         return "";
225     }
226     return resAdapter_->GetStringByName(resName);
227 }
228 
GetPluralString(uint32_t key,int count) const229 std::string ThemeConstants::GetPluralString(uint32_t key, int count) const
230 {
231     if (IsGlobalResource(key)) {
232         if (!resAdapter_) {
233             return "";
234         }
235         return resAdapter_->GetPluralString(key, count);
236     }
237     const auto& valueWrapper = GetValue(key);
238     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::STRING)) {
239         return "";
240     }
241     auto stringPair = valueWrapper.GetValue<std::string>("");
242     if (!stringPair.first) {
243         TAG_LOGW(AceLogTag::ACE_THEME, "Get theme pluralString error: %{public}u, type: %{public}u", key,
244             valueWrapper.type);
245     }
246     return stringPair.second;
247 }
248 
GetPluralStringByName(const std::string & resName,int count) const249 std::string ThemeConstants::GetPluralStringByName(const std::string& resName, int count) const
250 {
251     if (!resAdapter_) {
252         return "";
253     }
254     return resAdapter_->GetPluralStringByName(resName, count);
255 }
256 
GetStringArray(uint32_t key) const257 std::vector<std::string> ThemeConstants::GetStringArray(uint32_t key) const
258 {
259     if (IsGlobalResource(key)) {
260         if (!resAdapter_) {
261             return {};
262         }
263         return resAdapter_->GetStringArray(key);
264     }
265     return {};
266 }
267 
GetStringArrayByName(const std::string & resName) const268 std::vector<std::string> ThemeConstants::GetStringArrayByName(const std::string& resName) const
269 {
270     if (!resAdapter_) {
271         return {};
272     }
273     return resAdapter_->GetStringArrayByName(resName);
274 }
275 
GetMediaPath(uint32_t key) const276 std::string ThemeConstants::GetMediaPath(uint32_t key) const
277 {
278     if (IsGlobalResource(key)) {
279         if (!resAdapter_) {
280             return "";
281         }
282         return resAdapter_->GetMediaPath(key);
283     }
284     return "";
285 }
286 
GetMediaPathByName(const std::string & resName) const287 std::string ThemeConstants::GetMediaPathByName(const std::string& resName) const
288 {
289     if (!resAdapter_) {
290         return "";
291     }
292     return resAdapter_->GetMediaPathByName(resName);
293 }
294 
GetRawfile(const std::string & fileName) const295 std::string ThemeConstants::GetRawfile(const std::string& fileName) const
296 {
297     if (!resAdapter_) {
298         return "";
299     }
300     return resAdapter_->GetRawfile(fileName);
301 }
302 
GetRawFileDescription(const std::string & rawfileName,RawfileDescription & rawfileDescription) const303 bool ThemeConstants::GetRawFileDescription(const std::string& rawfileName, RawfileDescription& rawfileDescription) const
304 {
305     if (!resAdapter_) {
306         return false;
307     }
308     return resAdapter_->GetRawFileDescription(rawfileName, rawfileDescription);
309 }
310 
CloseRawFileDescription(const std::string & rawfileName) const311 bool ThemeConstants::CloseRawFileDescription(const std::string& rawfileName) const
312 {
313     if (!resAdapter_) {
314         return false;
315     }
316     return resAdapter_->CloseRawFileDescription(rawfileName);
317 }
318 
GetMediaById(const int32_t & resId,std::string & mediaPath) const319 bool ThemeConstants::GetMediaById(const int32_t& resId, std::string& mediaPath) const
320 {
321     if (!resAdapter_) {
322         return false;
323     }
324     return resAdapter_->GetMediaById(resId, mediaPath);
325 }
326 
GetBoolean(uint32_t key) const327 bool ThemeConstants::GetBoolean(uint32_t key) const
328 {
329     if (IsGlobalResource(key)) {
330         if (!resAdapter_) {
331             return false;
332         }
333         return resAdapter_->GetBoolean(key);
334     }
335     return false;
336 }
337 
GetBooleanByName(const std::string & resName) const338 bool ThemeConstants::GetBooleanByName(const std::string& resName) const
339 {
340     if (!resAdapter_) {
341         return false;
342     }
343     return resAdapter_->GetBooleanByName(resName);
344 }
345 
GetSymbolByName(const char * name) const346 uint32_t ThemeConstants::GetSymbolByName(const char* name) const
347 {
348     if (!resAdapter_) {
349         return ERROR_VALUE_UINT;
350     }
351     return resAdapter_->GetSymbolByName(name);
352 }
353 
GetIntArray(uint32_t key) const354 std::vector<uint32_t> ThemeConstants::GetIntArray(uint32_t key) const
355 {
356     if (IsGlobalResource(key)) {
357         if (!resAdapter_) {
358             return {};
359         }
360         return resAdapter_->GetIntArray(key);
361     }
362     return {};
363 }
364 
GetIntArrayByName(const std::string & resName) const365 std::vector<uint32_t> ThemeConstants::GetIntArrayByName(const std::string& resName) const
366 {
367     if (!resAdapter_) {
368         return {};
369     }
370     return resAdapter_->GetIntArrayByName(resName);
371 }
372 
GetResourceIdByName(const std::string & resName,const std::string & resType,uint32_t & resId) const373 bool ThemeConstants::GetResourceIdByName(const std::string& resName, const std::string& resType, uint32_t& resId) const
374 {
375     if (!resAdapter_) {
376         return false;
377     }
378     return resAdapter_->GetIdByName(resName, resType, resId);
379 }
380 
GetResourceId(uint32_t key) const381 InternalResource::ResourceId ThemeConstants::GetResourceId(uint32_t key) const
382 {
383     const auto& valueWrapper = GetValue(key);
384     if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::RESOURCE_ID)) {
385         return ERROR_VALUE_RESOURCE_ID;
386     }
387     auto resPair = valueWrapper.GetValue<InternalResource::ResourceId>(ERROR_VALUE_RESOURCE_ID);
388     if (!resPair.first) {
389         TAG_LOGW(
390             AceLogTag::ACE_THEME, "Get theme resourceId error: %{public}u, type: %{public}u", key, valueWrapper.type);
391     }
392     return resPair.second;
393 }
394 
GetPixelMap(uint32_t key) const395 std::shared_ptr<Media::PixelMap> ThemeConstants::GetPixelMap(uint32_t key) const
396 {
397     if (IsGlobalResource(key)) {
398         if (!resAdapter_) {
399             return nullptr;
400         }
401         return resAdapter_->GetPixelMap(key);
402     }
403     return nullptr;
404 }
405 
GetValue(uint32_t key) const406 ResValueWrapper ThemeConstants::GetValue(uint32_t key) const
407 {
408     // Find resource at custom styles.
409     auto customIter = customStyleMap_.find(key);
410     if (customIter != customStyleMap_.end()) {
411         return customIter->second;
412     }
413     // Find resource at prebuilt maps.
414     const auto platformConstants = ThemeConstants::GetPlatformConstants(key);
415     if (platformConstants == nullptr) {
416         return ERROR_VALUE;
417     }
418     if (platformConstants->type != ThemeConstantsType::REFERENCE) {
419         return *platformConstants;
420     }
421     // This value point to another style, recursively find target.
422     auto uintPtr = std::get_if<uint32_t>(&(platformConstants->value));
423     if (!uintPtr) {
424         return ERROR_VALUE;
425     }
426     // Copy reference value, blend alpha if need(reference color and current blendAlpha < 1.0).
427     auto refValue = GetValue(*uintPtr);
428     refValue.isPublic = platformConstants->isPublic;
429     auto blendAlpha = GetBlendAlpha(platformConstants->blendAlpha);
430     if ((refValue.type == ThemeConstantsType::COLOR) && (blendAlpha < BLEND_ALPHA_MAX)) {
431         auto colorPtr = std::get_if<Color>(&refValue.value);
432         if (!colorPtr) {
433             return ERROR_VALUE;
434         }
435         refValue.value = colorPtr->BlendOpacity(blendAlpha);
436     }
437     return refValue;
438 }
439 
GetBlendAlpha(const BlendAlpha & blendAlpha) const440 double ThemeConstants::GetBlendAlpha(const BlendAlpha& blendAlpha) const
441 {
442     auto doublePtr = std::get_if<double>(&blendAlpha);
443     if (doublePtr) {
444         return *doublePtr;
445     }
446     auto idPtr = std::get_if<uint32_t>(&blendAlpha);
447     if (idPtr) {
448         return ThemeConstants::GetDouble(*idPtr);
449     }
450     return BLEND_ALPHA_MAX;
451 }
452 
LoadTheme(int32_t themeId)453 void ThemeConstants::LoadTheme(int32_t themeId)
454 {
455     if (!resAdapter_) {
456         return;
457     }
458     currentThemeStyle_ = resAdapter_->GetTheme(themeId);
459     if (currentThemeStyle_) {
460         currentThemeStyle_->SetName(std::to_string(themeId));
461     }
462 }
463 
ParseTheme()464 void ThemeConstants::ParseTheme()
465 {
466     if (currentThemeStyle_) {
467         currentThemeStyle_->ParseContent();
468     }
469 }
470 
LoadCustomStyle(const RefPtr<AssetManager> & assetManager)471 void ThemeConstants::LoadCustomStyle(const RefPtr<AssetManager>& assetManager)
472 {
473     if (!assetManager) {
474         return;
475     }
476 
477     std::vector<std::string> files;
478 
479     assetManager->GetAssetList(STYLES_FOLDER_PATH, files);
480 
481     std::vector<std::string> fileNameList;
482     for (const auto& file : files) {
483         if (StringUtils::EndWith(file, FILE_TYPE_JSON)) {
484             fileNameList.emplace_back(file.substr(0, file.size() - (sizeof(FILE_TYPE_JSON) - 1)));
485         }
486     }
487 
488     std::vector<std::string> priorityFileList;
489     priorityFileList = AceResConfig::GetStyleResourceFallback(fileNameList);
490     for (auto fileIter = priorityFileList.rbegin(); fileIter != priorityFileList.rend(); ++fileIter) {
491         auto fileFullPath = STYLES_FOLDER_PATH + *fileIter + std::string(FILE_TYPE_JSON);
492         auto asset = assetManager->GetAsset(fileFullPath);
493         ThemeConstants::LoadFile(asset);
494     }
495 }
496 
ParseCustomStyle(const std::string & content)497 void ThemeConstants::ParseCustomStyle(const std::string& content)
498 {
499     auto rootJson = JsonUtil::ParseJsonString(content);
500     auto rootNode = rootJson->GetObject(CUSTOM_STYLE_ROOT_NAME);
501     if (rootNode->IsNull()) {
502         return;
503     }
504     auto child = rootNode->GetChild();
505     while (child && !child->IsNull()) {
506         const auto& key = child->GetKey();
507         const auto& value = child->GetString();
508         child = child->GetNext();
509         uint32_t styleId = StringUtils::StringToUint(key, UINT32_MAX);
510         if (styleId == UINT32_MAX) {
511             // Id format error.
512             continue;
513         }
514         const auto& oldValue = ThemeConstants::GetValue(styleId);
515         if (oldValue.type == ThemeConstantsType::ERROR) {
516             // Id not found.
517             continue;
518         }
519         if (!oldValue.isPublic) {
520             // Id is not public.
521             continue;
522         }
523         const auto& newValue = ThemeUtils::ParseStyleValue(styleId, oldValue, value);
524         // Replace default style with user custom style, use type to check parse success.
525         if (newValue.type == oldValue.type) {
526             customStyleMap_[styleId] = newValue;
527         }
528     }
529 }
530 
LoadFile(const RefPtr<Asset> & asset)531 void ThemeConstants::LoadFile(const RefPtr<Asset>& asset)
532 {
533     if (!asset) {
534         return;
535     }
536 
537     auto fileSize = asset->GetSize();
538     if (fileSize <= 0) {
539         return;
540     }
541     const auto& fileData = asset->GetData();
542     if (!fileData) {
543         return;
544     }
545     std::string styleContent;
546     styleContent.assign(fileData, fileData + fileSize);
547     if (styleContent.empty()) {
548         return;
549     }
550     ParseCustomStyle(styleContent);
551 }
552 
SetColorScheme(ColorScheme colorScheme)553 void ThemeConstants::SetColorScheme(ColorScheme colorScheme)
554 {
555     if (!currentThemeStyle_) {
556         return;
557     }
558     if (colorScheme == ColorScheme::SCHEME_TRANSPARENT) {
559         currentThemeStyle_->SetAttr(
560             THEME_ATTR_BG_COLOR, { .type = ThemeConstantsType::COLOR, .value = TRANSPARENT_BG_COLOR });
561     }
562 }
563 
564 } // namespace OHOS::Ace
565