1/*
2 * Copyright (c) 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/ohos/osal/resource_theme_style.h"
17
18#include <regex>
19#include <set>
20
21namespace OHOS::Ace {
22namespace {
23constexpr char COLOR_VALUE_PREFIX[] = "$color:";
24constexpr char MEDIA_VALUE_PREFIX[] = "/";
25constexpr char REF_ATTR_VALUE_KEY_WORD[] = "?theme:";
26
27constexpr char RES_TAG[] = "resource:///";
28// resource manager hap for system resource
29constexpr char RES_HAP_PREFIX[] = "ohos.global.systemres";
30#ifdef PREVIEW
31constexpr char RES_PATH_TAG[] = "file://";
32// resource manager hap absolute path, as resource manager api don't return
33constexpr char RES_HAP_PATH[] = "../resources/";
34#else
35constexpr char RES_PATH_TAG[] = "file:///";
36// resource manager hap absolute path, as resource manager api don't return
37constexpr char RES_HAP_PATH[] = "/data/storage/el1/bundle/ohos.global.systemres/ohos.global.systemres/assets/";
38#endif
39
40const std::string DIMENSION_PATTERN = R"(^([+-]?\d+(\.\d+)?)(px|fp|lpx|vp|%)?)";
41constexpr int32_t WAIT_FOR_TIME = 50;
42static const std::set<std::string> stringAttrs = {
43    "attribute_text_font_family_regular",
44    "attribute_text_font_family_medium",
45    "description_current_location",
46    "description_add_location",
47    "description_select_location",
48    "description_share_location",
49    "description_send_location",
50    "description_locating",
51    "description_location",
52    "description_send_current_location",
53    "description_relocation",
54    "description_punch_in",
55    "description_current_position",
56    "description_paste",
57    "description_download",
58    "description_download_file",
59    "description_save",
60    "description_save_image",
61    "description_save_file",
62    "description_download_and_share",
63    "description_receive",
64    "description_continue_to_receive",
65    "description_save_to_gallery",
66    "description_export_to_gallery",
67    "description_quick_save_to_gallery",
68    "description_quick_resave_to_gallery",
69    "draggable",
70    "divider_shadow_enable",
71    "camera_input",
72    "menu_bg_blur_effect_enable",
73    "menu_double_border_enable",
74    "section_unfocus_effect_enable",
75    "section_unfocus_color",
76    "sheet_type",
77    "multiple_dialog_display",
78    "menu_expand_display",
79    "popup_double_border_enable",
80    "dialog_expand_display",
81    "show_password_directly",
82    "textfield_show_handle",
83    "dialog_radius_level10",
84    "dialog_icon_primary",
85    "dialog_font_primary",
86    "menu_has_filter",
87    "calendar_picker_dialog_button_transparent",
88    "calendar_picker_dialog_divider_transparent",
89    "textfield_accessibility_property_clear",
90    "textfield_accessibility_show_password",
91    "textfield_accessibility_hide_password",
92    "rich_editor_show_handle",
93    "text_show_handle",
94    "textfield_show_password_button",
95    "textfield_hide_password_button",
96    "textfield_has_showed_password",
97    "textfield_has_hidden_password",
98    "calendar_picker_mon",
99    "calendar_picker_tue",
100    "calendar_picker_wed",
101    "calendar_picker_thu",
102    "calendar_picker_fri",
103    "calendar_picker_sat",
104    "calendar_picker_sun",
105    "slider_accessibility_selected",
106    "slider_accessibility_unselected",
107    "slider_accessibility_unselectedDesc",
108    "slider_accessibility_disabledDesc",
109    "textfield_writting_bundle_name",
110    "textfield_writting_ability_name",
111    "rich_editor_writting_bundle_name",
112    "rich_editor_writting_ability_name",
113    "textfield_writting_is_support",
114    "rich_editor_writting_is_support",
115    "ai_write_menu_name",
116    "textfield_accessibility_clear",
117    "pass_point"
118};
119
120void ParseNumberUnit(const std::string& value, std::string& number, std::string& unit)
121{
122    std::regex regex(DIMENSION_PATTERN);
123    std::smatch results;
124    if (std::regex_search(value, results, regex)) {
125        number = results[1];
126        // The unit is in the 3rd sub-match. If the value doesn't have unit,
127        // the 3rd match result is empty.
128        unit = results[3];
129    }
130}
131
132DimensionUnit ParseDimensionUnit(const std::string& unit)
133{
134    if (unit == "px") {
135        return DimensionUnit::PX;
136    } else if (unit == "fp") {
137        return DimensionUnit::FP;
138    } else if (unit == "lpx") {
139        return DimensionUnit::LPX;
140    } else if (unit == "%") {
141        return DimensionUnit::PERCENT;
142    } else {
143        return DimensionUnit::VP;
144    }
145}
146}
147
148void ResourceThemeStyle::ParseContent()
149{
150    for (auto& [attrName, attrValue] : rawAttrs_) {
151        if (attrName.empty() || attrValue.empty()) {
152            continue;
153        }
154        if (stringAttrs.find(attrName) != stringAttrs.end()) {
155            // string
156            attributes_[attrName] = { .type = ThemeConstantsType::STRING, .value = attrValue };
157            continue;
158        }
159        if (attrValue.front() == '#' || attrValue.find(COLOR_VALUE_PREFIX) != std::string::npos) {
160            // color
161            attributes_[attrName] = { .type = ThemeConstantsType::COLOR, .value = Color::FromString(attrValue) };
162        } else if (attrValue.find(MEDIA_VALUE_PREFIX) != std::string::npos) {
163            OnParseResourceMedia(attrName, attrValue);
164        } else if (attrValue.find(REF_ATTR_VALUE_KEY_WORD) != std::string::npos) {
165            attributes_[attrName] = { .type = ThemeConstantsType::REFERENCE_ATTR, .value = attrValue };
166        } else {
167            // int & double & dimension
168            std::string number;
169            std::string unit;
170            ParseNumberUnit(attrValue, number, unit);
171            if (number.empty()) {
172                continue;
173            } else if (!unit.empty()) {
174                attributes_[attrName] = { .type = ThemeConstantsType::DIMENSION,
175                    .value = Dimension(std::atof(number.c_str()), ParseDimensionUnit(unit)) };
176            } else if (number.find(".") == std::string::npos) {
177                attributes_[attrName] = { .type = ThemeConstantsType::INT, .value = std::atoi(number.c_str()) };
178            } else {
179                attributes_[attrName] = { .type = ThemeConstantsType::DOUBLE, .value = std::atof(number.c_str()) };
180            }
181        }
182    }
183    OnParseStyle();
184}
185
186void ResourceThemeStyle::OnParseStyle()
187{
188    for (auto& [patternName, patternMap]: patternAttrs_) {
189        auto patternStyle = AceType::MakeRefPtr<ResourceThemeStyle>(resAdapter_);
190        patternStyle->SetName(patternName);
191        patternStyle->parentStyle_ = AceType::WeakClaim(this);
192        patternStyle->rawAttrs_ = patternMap;
193        patternStyle->ParseContent();
194        attributes_[patternName] = { .type = ThemeConstantsType::PATTERN,
195            .value = RefPtr<ThemeStyle>(std::move(patternStyle)) };
196    }
197}
198
199void ResourceThemeStyle::OnParseResourceMedia(const std::string& attrName, const std::string& attrValue)
200{
201    std::string mediaPath;
202    if (SystemProperties::GetUnZipHap()) {
203        mediaPath = RES_PATH_TAG;
204        if (attrValue.find(RES_HAP_PREFIX) == std::string::npos) {
205            mediaPath.append(RES_HAP_PATH);
206        }
207#ifdef PREVIEW
208        auto pos = attrValue.find(MEDIA_VALUE_PREFIX);
209        if (pos == std::string::npos) {
210            return;
211        }
212        mediaPath += attrValue.substr(pos + 1);
213#else
214        mediaPath += attrValue;
215#endif
216    } else {
217        // hap is not unzip, should use resource name to read file
218        auto pos = attrValue.find_last_of(MEDIA_VALUE_PREFIX);
219        if (pos == std::string::npos) {
220            LOGW("resource media invalid:[%{public}s, %{public}s]", attrName.c_str(), attrValue.c_str());
221            return;
222        }
223        mediaPath = std::string(RES_TAG) + attrValue.substr(pos + 1);
224    }
225    attributes_[attrName] = { .type = ThemeConstantsType::STRING, .value = mediaPath };
226}
227
228void ResourceThemeStyle::CheckThemeStyleLoaded(const std::string& patternName)
229{
230    if (!CheckThemeStyle(patternName)) {
231        return;
232    }
233    if (future_.valid()) {
234        future_.wait_until(std::chrono::system_clock::now() + std::chrono::milliseconds(WAIT_FOR_TIME));
235    }
236}
237} // namespace OHOS::Ace
238