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
GetDimension(uint32_t key) const115 Dimension ThemeConstants::GetDimension(uint32_t key) const
116 {
117 if (IsGlobalResource(key)) {
118 if (!resAdapter_) {
119 return ERROR_VALUE_DIMENSION;
120 }
121 return resAdapter_->GetDimension(key);
122 }
123 const auto& valueWrapper = GetValue(key);
124 if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::DIMENSION)) {
125 return ERROR_VALUE_DIMENSION;
126 }
127 auto dimensionPair = valueWrapper.GetValue<Dimension>(ERROR_VALUE_DIMENSION);
128 if (!dimensionPair.first) {
129 LOGE("GetDimension error: %{public}u, type: %{public}u", key, valueWrapper.type);
130 }
131 return dimensionPair.second;
132 }
133
GetInt(uint32_t key) const134 int32_t ThemeConstants::GetInt(uint32_t key) const
135 {
136 if (IsGlobalResource(key)) {
137 if (!resAdapter_) {
138 return ERROR_VALUE_INT;
139 }
140 return resAdapter_->GetInt(key);
141 }
142 const auto& valueWrapper = GetValue(key);
143 if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::INT)) {
144 return ERROR_VALUE_INT;
145 }
146 auto intPair = valueWrapper.GetValue<int32_t>(ERROR_VALUE_INT);
147 if (!intPair.first) {
148 LOGE("GetInt error: %{public}u, type: %{public}u", key, valueWrapper.type);
149 }
150 return intPair.second;
151 }
152
GetDouble(uint32_t key) const153 double ThemeConstants::GetDouble(uint32_t key) const
154 {
155 if (IsGlobalResource(key)) {
156 if (!resAdapter_) {
157 return ERROR_VALUE_DOUBLE;
158 }
159 return resAdapter_->GetDouble(key);
160 }
161 const auto& valueWrapper = GetValue(key);
162 if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::DOUBLE)) {
163 return ERROR_VALUE_DOUBLE;
164 }
165 auto doublePair = valueWrapper.GetValue<double>(ERROR_VALUE_DOUBLE);
166 if (!doublePair.first) {
167 LOGE("GetDouble error: %{public}u, type: %{public}u", key, valueWrapper.type);
168 }
169 return doublePair.second;
170 }
171
GetString(uint32_t key) const172 std::string ThemeConstants::GetString(uint32_t key) const
173 {
174 if (IsGlobalResource(key)) {
175 if (!resAdapter_) {
176 return "";
177 }
178 return resAdapter_->GetString(key);
179 }
180 const auto& valueWrapper = GetValue(key);
181 if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::STRING)) {
182 return "";
183 }
184 auto stringPair = valueWrapper.GetValue<std::string>("");
185 if (!stringPair.first) {
186 LOGE("GetString error: %{public}u, type: %{public}u", key, valueWrapper.type);
187 }
188 return stringPair.second;
189 }
190
GetPluralString(uint32_t key,int count) const191 std::string ThemeConstants::GetPluralString(uint32_t key, int count) const
192 {
193 if (IsGlobalResource(key)) {
194 if (!resAdapter_) {
195 return "";
196 }
197 return resAdapter_->GetPluralString(key, count);
198 }
199 const auto& valueWrapper = GetValue(key);
200 if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::STRING)) {
201 return "";
202 }
203 auto stringPair = valueWrapper.GetValue<std::string>("");
204 if (!stringPair.first) {
205 LOGE("GetPluralString error: %{public}u, type: %{public}u", key, valueWrapper.type);
206 }
207 return stringPair.second;
208 }
209
GetStringArray(uint32_t key) const210 std::vector<std::string> ThemeConstants::GetStringArray(uint32_t key) const
211 {
212 if (IsGlobalResource(key)) {
213 if (!resAdapter_) {
214 return {};
215 }
216 return resAdapter_->GetStringArray(key);
217 }
218 return {};
219 }
220
GetMediaPath(uint32_t key) const221 std::string ThemeConstants::GetMediaPath(uint32_t key) const
222 {
223 if (IsGlobalResource(key)) {
224 if (!resAdapter_) {
225 return "";
226 }
227 return resAdapter_->GetMediaPath(key);
228 }
229 return "";
230 }
231
GetRawfile(const std::string & fileName) const232 std::string ThemeConstants::GetRawfile(const std::string& fileName) const
233 {
234 if (!resAdapter_) {
235 return "";
236 }
237 return resAdapter_->GetRawfile(fileName);
238 }
239
GetBoolean(uint32_t key) const240 bool ThemeConstants::GetBoolean(uint32_t key) const
241 {
242 if (IsGlobalResource(key)) {
243 if (!resAdapter_) {
244 return false;
245 }
246 return resAdapter_->GetBoolean(key);
247 }
248 return false;
249 }
250
GetIntArray(uint32_t key) const251 std::vector<uint32_t> ThemeConstants::GetIntArray(uint32_t key) const
252 {
253 if (IsGlobalResource(key)) {
254 if (!resAdapter_) {
255 return {};
256 }
257 return resAdapter_->GetIntArray(key);
258 }
259 return {};
260 }
261
GetResourceIdByName(const std::string & resName,const std::string & resType,uint32_t & resId) const262 bool ThemeConstants::GetResourceIdByName(const std::string& resName, const std::string& resType, uint32_t& resId) const
263 {
264 if (!resAdapter_) {
265 return false;
266 }
267 return resAdapter_->GetIdByName(resName, resType, resId);
268 }
269
GetResourceId(uint32_t key) const270 InternalResource::ResourceId ThemeConstants::GetResourceId(uint32_t key) const
271 {
272 const auto& valueWrapper = GetValue(key);
273 if (!ValueTypeMatch(valueWrapper, key, ThemeConstantsType::RESOURCE_ID)) {
274 return ERROR_VALUE_RESOURCE_ID;
275 }
276 auto resPair = valueWrapper.GetValue<InternalResource::ResourceId>(ERROR_VALUE_RESOURCE_ID);
277 if (!resPair.first) {
278 LOGE("GetResourceId error: %{public}u, type: %{public}u", key, valueWrapper.type);
279 }
280 return resPair.second;
281 }
282
GetValue(uint32_t key) const283 ResValueWrapper ThemeConstants::GetValue(uint32_t key) const
284 {
285 // Find resource at custom styles.
286 auto customIter = customStyleMap_.find(key);
287 if (customIter != customStyleMap_.end()) {
288 return customIter->second;
289 }
290 // Find resource at prebuilt maps.
291 const auto platformConstants = ThemeConstants::GetPlatformConstants(key);
292
293 if (platformConstants == nullptr) {
294 return ERROR_VALUE;
295 }
296 if (platformConstants->type != ThemeConstantsType::REFERENCE) {
297 return *platformConstants;
298 }
299 // This value point to another style, recursively find target.
300 auto uintPtr = std::get_if<uint32_t>(&(platformConstants->value));
301 if (!uintPtr) {
302 return ERROR_VALUE;
303 }
304 // Copy reference value, blend alpha if need(reference color and current blendAlpha < 1.0).
305 auto refValue = GetValue(*uintPtr);
306 refValue.isPublic = platformConstants->isPublic;
307 auto blendAlpha = GetBlendAlpha(platformConstants->blendAlpha);
308 if ((refValue.type == ThemeConstantsType::COLOR) && (blendAlpha < BLEND_ALPHA_MAX)) {
309 auto colorPtr = std::get_if<Color>(&refValue.value);
310 if (!colorPtr) {
311 return ERROR_VALUE;
312 }
313 refValue.value = colorPtr->BlendOpacity(blendAlpha);
314 }
315 return refValue;
316 }
317
GetBlendAlpha(const BlendAlpha & blendAlpha) const318 double ThemeConstants::GetBlendAlpha(const BlendAlpha& blendAlpha) const
319 {
320 auto doublePtr = std::get_if<double>(&blendAlpha);
321 if (doublePtr) {
322 return *doublePtr;
323 }
324 auto idPtr = std::get_if<uint32_t>(&blendAlpha);
325 if (idPtr) {
326 return ThemeConstants::GetDouble(*idPtr);
327 }
328 return BLEND_ALPHA_MAX;
329 }
330
LoadTheme(int32_t themeId)331 void ThemeConstants::LoadTheme(int32_t themeId)
332 {
333 if (!resAdapter_) {
334 LOGE("resAdapter_ is null, load theme resource failed!");
335 return;
336 }
337 currentThemeStyle_ = resAdapter_->GetTheme(themeId);
338 if (currentThemeStyle_) {
339 currentThemeStyle_->SetName(std::to_string(themeId));
340 }
341 }
342
ParseTheme()343 void ThemeConstants::ParseTheme()
344 {
345 if (currentThemeStyle_) {
346 currentThemeStyle_->ParseContent();
347 }
348 }
349
LoadCustomStyle(const RefPtr<AssetManager> & assetManager)350 void ThemeConstants::LoadCustomStyle(const RefPtr<AssetManager>& assetManager)
351 {
352 if (!assetManager) {
353 LOGE("AssetManager is null, load custom style failed!");
354 return;
355 }
356
357 std::vector<std::string> files;
358
359 assetManager->GetAssetList(STYLES_FOLDER_PATH, files);
360
361 std::vector<std::string> fileNameList;
362 for (const auto& file : files) {
363 if (StringUtils::EndWith(file, FILE_TYPE_JSON)) {
364 fileNameList.emplace_back(file.substr(0, file.size() - (sizeof(FILE_TYPE_JSON) - 1)));
365 }
366 }
367
368 std::vector<std::string> priorityFileList;
369 priorityFileList = AceResConfig::GetStyleResourceFallback(fileNameList);
370 for (auto fileIter = priorityFileList.rbegin(); fileIter != priorityFileList.rend(); ++fileIter) {
371 auto fileFullPath = STYLES_FOLDER_PATH + *fileIter + std::string(FILE_TYPE_JSON);
372 auto asset = assetManager->GetAsset(fileFullPath);
373 ThemeConstants::LoadFile(asset);
374 }
375 }
376
ParseCustomStyle(const std::string & content)377 void ThemeConstants::ParseCustomStyle(const std::string& content)
378 {
379 auto rootJson = JsonUtil::ParseJsonString(content);
380 auto rootNode = rootJson->GetObject(CUSTOM_STYLE_ROOT_NAME);
381 if (rootNode->IsNull()) {
382 LOGE("Load custom style, root node 'style' not found.");
383 return;
384 }
385 auto child = rootNode->GetChild();
386 while (child && !child->IsNull()) {
387 const auto& key = child->GetKey();
388 const auto& value = child->GetString();
389 child = child->GetNext();
390 uint32_t styleId = StringUtils::StringToUint(key, UINT32_MAX);
391 if (styleId == UINT32_MAX) {
392 // Id format error.
393 continue;
394 }
395 const auto& oldValue = ThemeConstants::GetValue(styleId);
396 if (oldValue.type == ThemeConstantsType::ERROR) {
397 // Id not found.
398 continue;
399 }
400 if (!oldValue.isPublic) {
401 // Id is not public.
402 continue;
403 }
404 const auto& newValue = ThemeUtils::ParseStyleValue(styleId, oldValue, value);
405 // Replace default style with user custom style, use type to check parse success.
406 if (newValue.type == oldValue.type) {
407 customStyleMap_[styleId] = newValue;
408 }
409 }
410 }
411
LoadFile(const RefPtr<Asset> & asset)412 void ThemeConstants::LoadFile(const RefPtr<Asset>& asset)
413 {
414 if (!asset) {
415 LOGD("No custom style found.");
416 return;
417 }
418
419 auto fileSize = asset->GetSize();
420 if (fileSize <= 0) {
421 LOGD("Load custom style, file is empty.");
422 return;
423 }
424 const auto& fileData = asset->GetData();
425 if (!fileData) {
426 LOGD("Load custom style, file data is null.");
427 return;
428 }
429 std::string styleContent;
430 styleContent.assign(fileData, fileData + fileSize);
431 if (styleContent.empty()) {
432 return;
433 }
434 ParseCustomStyle(styleContent);
435 }
436
SetColorScheme(ColorScheme colorScheme)437 void ThemeConstants::SetColorScheme(ColorScheme colorScheme)
438 {
439 if (!currentThemeStyle_) {
440 return;
441 }
442 if (colorScheme == ColorScheme::SCHEME_TRANSPARENT) {
443 currentThemeStyle_->SetAttr(
444 THEME_ATTR_BG_COLOR, { .type = ThemeConstantsType::COLOR, .value = TRANSPARENT_BG_COLOR });
445 }
446 }
447
448 } // namespace OHOS::Ace
449