• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/components/checkable/render_radio.h"
17 
18 #include "base/json/json_util.h"
19 #include "base/log/event_report.h"
20 #include "core/animation/curves.h"
21 #include "core/common/frontend.h"
22 #include "core/components/text/text_component.h"
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 constexpr int DEFAULT_RADIO_ANIMATION_DURATION = 300;
28 constexpr double DEFAULT_MID_TIME_SLOT = 0.5;
29 constexpr double DEFAULT_END_TIME_SLOT = 1.0;
30 constexpr double DEFAULT_SHRINK_TIME_SLOT = 0.9;
31 
32 } // namespace
33 
Update(const RefPtr<Component> & component)34 void RenderRadio::Update(const RefPtr<Component>& component)
35 {
36     RenderCheckable::Update(component);
37     const auto& radio = AceType::DynamicCast<RadioComponent<std::string>>(component);
38     component_ = radio;
39     if (!radio) {
40         LOGE("cast to radio component failed");
41         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
42         return;
43     }
44     radio->SetGroupValueUpdateHandler([weak = AceType::WeakClaim(this)](const std::string& groupValue) {
45         LOGD("group value changed");
46         auto renderRadio = weak.Upgrade();
47         if (renderRadio && renderRadio->UpdateGroupValue(groupValue)) {
48             renderRadio->MarkNeedRender();
49         }
50     });
51 
52     radioInnerSizeRatio_ = radio->GetRadioInnerSizeRatio();
53 
54     if (radio->GetUpdateType() == UpdateType::ALL) {
55         radioValue_ = radio->GetValue();
56         groupValue_ = radio->GetGroupValue();
57     }
58     bool originChecked = radio->GetOriginChecked();
59     if (!groupValue_.empty()) {
60         checked_ = radioValue_ == groupValue_;
61     } else {
62         checked_ = originChecked;
63     }
64 
65     ApplyRestoreInfo();
66 
67     auto theme = GetTheme<RadioTheme>();
68     if (theme) {
69         borderWidth_ = theme->GetBorderWidth();
70     }
71     UpdateUIStatus();
72     // set check animation engine
73     if (!onController_) {
74         onController_ = CREATE_ANIMATOR(GetContext());
75         onController_->AddStartListener(Animator::StatusCallback([weak = AceType::WeakClaim(this)]() {
76             auto radio = weak.Upgrade();
77             if (radio) {
78                 radio->OnAnimationStart();
79             }
80         }));
81     }
82     // set uncheck animation engine
83     if (!offController_) {
84         offController_ = CREATE_ANIMATOR(GetContext());
85         offController_->AddStopListener(Animator::StatusCallback([weak = AceType::WeakClaim(this)]() {
86             auto radio = weak.Upgrade();
87             if (radio) {
88                 radio->OffAnimationEnd();
89             }
90         }));
91     }
92     groupValueChangedListener_ = radio->GetGroupValueChangedListener();
93     pointScale_ = radio->GetRadioInnerSizeRatio();
94     UpdateAccessibilityAttr();
95 }
96 
UpdateAccessibilityAttr() const97 void RenderRadio::UpdateAccessibilityAttr() const
98 {
99     auto accessibilityNode = GetAccessibilityNode().Upgrade();
100     if (!accessibilityNode) {
101         return;
102     }
103     accessibilityNode->SetCheckedState(checked_);
104     if (accessibilityNode->GetClicked()) {
105         accessibilityNode->SetClicked(false);
106         auto context = context_.Upgrade();
107         if (context) {
108             AccessibilityEvent radioEvent;
109             radioEvent.nodeId = accessibilityNode->GetNodeId();
110             radioEvent.eventType = "click";
111             context->SendEventToAccessibility(radioEvent);
112         }
113     }
114 }
115 
UpdateGroupValue(const std::string & groupValue)116 bool RenderRadio::UpdateGroupValue(const std::string& groupValue)
117 {
118     LOGD("update group value");
119     groupValue_ = groupValue;
120 
121     bool needRender = false;
122 
123     if (!checked_ && isClicked_) {
124         checked_ = true;
125         isClicked_ = false;
126         needRender = true;
127         UpdateUIStatus();
128     } else if (checked_) {
129         checked_ = false;
130         needRender = true;
131         UpdateAnimation(false);
132         offController_->Play();
133     }
134     std::string checked = checked_ ? "true" : "false";
135     std::string result = std::string("\"change\",{\"checked\":")
136                              .append(checked)
137                              .append(",\"value\":\"")
138                              .append(groupValue_)
139                              .append("\"},null");
140     auto context = context_.Upgrade();
141     if (context && context->GetFrontend() && context->GetFrontendType() == FrontendType::JS_CARD) {
142         auto json = JsonUtil::Create(true);
143         json->Put("checkedItem", groupValue_.c_str());
144         result = json->ToString();
145     }
146     OnHandleChangedResult(result);
147     if (changeEvent_) {
148         changeEvent_(result);
149     }
150     if (valueChangeEvent_) {
151         valueChangeEvent_(groupValue_);
152     }
153     return needRender;
154 }
155 
PerformLayout()156 void RenderRadio::PerformLayout()
157 {
158     RenderCheckable::PerformLayout();
159     outCircleRadius_ = drawSize_.Width() / 2.0;
160     innerCircleRadius_ = outCircleRadius_ * radioInnerSizeRatio_;
161 }
162 
HandleClick()163 void RenderRadio::HandleClick()
164 {
165     if (!checked_) {
166         isClicked_ = true;
167         if (groupValueChangedListener_) {
168             LOGE("groupValueChangedListener_");
169             groupValueChangedListener_(radioValue_);
170         }
171     }
172     UpdateAnimation(true);
173     onController_->Play();
174 
175     if (clickEvent_) {
176         clickEvent_();
177     }
178     if (onClick_) {
179         onClick_();
180     }
181     if (onChange_) {
182         onChange_(checked_);
183     }
184 }
185 
OffAnimationEnd()186 void RenderRadio::OffAnimationEnd()
187 {
188     // after the animation stopped, set the stage back to unchecked
189     UpdateUIStatus();
190     UpdateAccessibilityAttr();
191 }
192 
OnAnimationStart()193 void RenderRadio::OnAnimationStart()
194 {
195     // set the stage to checked and then start animation
196     UpdateAccessibilityAttr();
197 }
198 
UpdateAnimation(bool isOn)199 void RenderRadio::UpdateAnimation(bool isOn)
200 {
201     if (!onController_ || !offController_) {
202         LOGE("update animation failed due to controller is null");
203         return;
204     }
205     RefPtr<KeyframeAnimation<double>> shrinkEngine = AceType::MakeRefPtr<KeyframeAnimation<double>>();
206     RefPtr<KeyframeAnimation<double>> selectEngine = AceType::MakeRefPtr<KeyframeAnimation<double>>();
207     onController_->ClearInterpolators();
208     offController_->ClearInterpolators();
209     auto shrinkFrameStart = AceType::MakeRefPtr<Keyframe<double>>(0.0, 1.0);
210     auto shrinkFrameMid = AceType::MakeRefPtr<Keyframe<double>>(DEFAULT_MID_TIME_SLOT, DEFAULT_SHRINK_TIME_SLOT);
211     shrinkFrameMid->SetCurve(Curves::FRICTION);
212     auto shrinkFrameEnd = AceType::MakeRefPtr<Keyframe<double>>(DEFAULT_END_TIME_SLOT, 1.0);
213     shrinkFrameEnd->SetCurve(Curves::FRICTION);
214     shrinkEngine->AddKeyframe(shrinkFrameStart);
215     shrinkEngine->AddKeyframe(shrinkFrameMid);
216     shrinkEngine->AddKeyframe(shrinkFrameEnd);
217     shrinkEngine->AddListener(Animation<double>::ValueCallback([weak = AceType::WeakClaim(this)](double value) {
218         auto radio = weak.Upgrade();
219         if (radio) {
220             radio->totalScale_ = value;
221             radio->MarkNeedRender();
222         }
223     }));
224 
225     auto selectFrameStart = AceType::MakeRefPtr<Keyframe<double>>(0.0, isOn ? 1.0 : radioInnerSizeRatio_);
226     auto selectFrameMid = AceType::MakeRefPtr<Keyframe<double>>(DEFAULT_MID_TIME_SLOT, 0.0);
227     selectFrameMid->SetCurve(Curves::FRICTION);
228     auto selectFrameEnd =
229         AceType::MakeRefPtr<Keyframe<double>>(DEFAULT_END_TIME_SLOT, isOn ? radioInnerSizeRatio_ : 1.0);
230     selectFrameEnd->SetCurve(Curves::FRICTION);
231     selectEngine->AddKeyframe(selectFrameStart);
232     selectEngine->AddKeyframe(selectFrameMid);
233     selectEngine->AddKeyframe(selectFrameEnd);
234     selectEngine->AddListener(Animation<double>::ValueCallback([weak = AceType::WeakClaim(this)](double value) {
235         auto radio = weak.Upgrade();
236         if (radio) {
237             radio->pointScale_ = value;
238             radio->MarkNeedRender();
239         }
240     }));
241     if (isOn) {
242         onController_->AddInterpolator(shrinkEngine);
243         onController_->AddInterpolator(selectEngine);
244         onController_->SetDuration(DEFAULT_RADIO_ANIMATION_DURATION);
245     } else {
246         offController_->AddInterpolator(shrinkEngine);
247         offController_->AddInterpolator(selectEngine);
248         offController_->SetDuration(DEFAULT_RADIO_ANIMATION_DURATION);
249     }
250 }
251 
ProvideRestoreInfo()252 std::string RenderRadio::ProvideRestoreInfo()
253 {
254     auto jsonObj = JsonUtil::Create(true);
255     jsonObj->Put("checked", checked_);
256     jsonObj->Put("radioValue", radioValue_.c_str());
257     jsonObj->Put("groupValue", groupValue_.c_str());
258     return jsonObj->ToString();
259 }
260 
ApplyRestoreInfo()261 void RenderRadio::ApplyRestoreInfo()
262 {
263     if (GetRestoreInfo().empty()) {
264         return;
265     }
266     auto info = JsonUtil::ParseJsonString(GetRestoreInfo());
267     if (!info->IsValid() || !info->IsObject()) {
268         LOGW("RenderRadio:: restore info is invalid");
269         return;
270     }
271 
272     auto jsonchecked = info->GetValue("checked");
273     auto jsonradioValue = info->GetValue("radioValue");
274     auto jsongroupValue = info->GetValue("groupValue");
275 
276     checked_ = jsonchecked->GetBool();
277     radioValue_ = jsonradioValue->GetString();
278     groupValue_ = jsongroupValue->GetString();
279     SetRestoreInfo("");
280 }
281 
282 } // namespace OHOS::Ace
283