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