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_v2/pattern_lock/render_pattern_lock.h"
17 
18 #include "base/log/event_report.h"
19 #include "core/common/font_manager.h"
20 #include "core/components/common/properties/alignment.h"
21 #include "core/event/ace_event_helper.h"
22 
23 namespace OHOS::Ace::V2 {
RenderPatternLock()24 RenderPatternLock::RenderPatternLock()
25 {
26     touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
27     auto weak = AceType::WeakClaim(this);
28     touchRecognizer_->SetOnTouchDown([weak](const TouchEventInfo& info) {
29         auto patternLock = weak.Upgrade();
30         if (patternLock && !info.GetTouches().empty()) {
31             const auto& locationInfo = info.GetTouches().front();
32             double moveDeltaX = locationInfo.GetLocalLocation().GetX();
33             double moveDeltaY = locationInfo.GetLocalLocation().GetY();
34             Offset touchPoint;
35             touchPoint.SetX(moveDeltaX);
36             touchPoint.SetY(moveDeltaY);
37             patternLock->HandleCellTouchDown(touchPoint);
38         }
39     });
40     touchRecognizer_->SetOnTouchMove([weak](const TouchEventInfo& info) {
41         auto patternLock = weak.Upgrade();
42         if (patternLock && !info.GetTouches().empty()) {
43             const auto& locationInfo = info.GetTouches().front();
44             double moveDeltaX = locationInfo.GetLocalLocation().GetX();
45             double moveDeltaY = locationInfo.GetLocalLocation().GetY();
46             Offset touchPoint;
47             touchPoint.SetX(moveDeltaX);
48             touchPoint.SetY(moveDeltaY);
49             patternLock->HandleCellTouchMove(touchPoint);
50         }
51     });
52     touchRecognizer_->SetOnTouchUp([weak](const TouchEventInfo& info) {
53         auto patternLock = weak.Upgrade();
54         if (patternLock) {
55             patternLock->HandleCellTouchUp();
56         }
57     });
58     touchRecognizer_->SetOnTouchCancel([weak](const TouchEventInfo& info) {
59         auto patternLock = weak.Upgrade();
60         if (patternLock) {
61             patternLock->HandleCellTouchCancel();
62         }
63     });
64 }
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)65 void RenderPatternLock::OnTouchTestHit(
66     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
67 {
68     touchRecognizer_->SetCoordinateOffset(coordinateOffset);
69     result.emplace_back(touchRecognizer_);
70 }
HandleCellTouchDown(const Offset & offset)71 void RenderPatternLock::HandleCellTouchDown(const Offset& offset)
72 {
73     if (!CheckAutoReset()) {
74         return;
75     }
76     HandleReset();
77     cellCenter_ = offset;
78     bool isAdd = false;
79     for (int16_t i = 0; i < COL_COUNT && !isAdd; i++) {
80         for (int16_t j = 0; j < COL_COUNT && !isAdd; j++) {
81             isAdd = AddChoosePoint(offset, i + 1, j + 1);
82         }
83     }
84     MarkNeedRender();
85     isMoveEventValid_ = true;
86 }
HandleCellTouchMove(const Offset & offset)87 void RenderPatternLock::HandleCellTouchMove(const Offset& offset)
88 {
89     if (!isMoveEventValid_) {
90         return;
91     }
92     cellCenter_ = offset;
93     bool isAdd = false;
94     for (int16_t i = 0; i < COL_COUNT && !isAdd; i++) {
95         for (int16_t j = 0; j < COL_COUNT && !isAdd; j++) {
96             isAdd = AddChoosePoint(offset, i + 1, j + 1);
97         }
98     }
99     MarkNeedRender();
100 }
HandleCellTouchUp()101 void RenderPatternLock::HandleCellTouchUp()
102 {
103     if (!CheckAutoReset()) {
104         return;
105     }
106     animator_->Finish();
107     isMoveEventValid_ = false;
108     std::vector<int> chooseCellVec;
109     for (auto it = choosePoint_.begin(); it != choosePoint_.end(); it++) {
110         chooseCellVec.push_back((*it).GetCode());
111     }
112     if (callbackForJS_) {
113         auto event = std::make_shared<PatternCompleteEvent>(chooseCellVec);
114         if (event) {
115             callbackForJS_(event);
116         }
117     }
118     animator_->SetDuration(DOWN_DURATION);
119     animator_->Forward();
120     MarkNeedRender();
121 }
122 
HandleCellTouchCancel()123 void RenderPatternLock::HandleCellTouchCancel()
124 {
125     HandleCellTouchUp();
126 }
UpdateAttr(const RefPtr<Component> & component)127 void RenderPatternLock::UpdateAttr(const RefPtr<Component>& component)
128 {
129     const auto& patternLockComponent = AceType::DynamicCast<PatternLockComponent>(component);
130     if (!patternLockComponent) {
131         LOGE("PatternLockComponent is null!");
132         return;
133     }
134     sideLength_ = patternLockComponent->GetSideLength().Value() < 0.0 ? 0.0_vp : patternLockComponent->GetSideLength();
135     double cSideLength = patternLockComponent->GetSideLength().ConvertToVp();
136     double cCircleRadius = patternLockComponent->GetCircleRadius().ConvertToVp();
137     const int16_t radiusCount = COL_COUNT * RADIUS_TO_DIAMETER;
138     double handleCircleRadius = cCircleRadius > cSideLength / SCALE_SELECTED_CIRCLE_RADIUS / radiusCount
139                                     ? cSideLength / SCALE_SELECTED_CIRCLE_RADIUS / radiusCount
140                                     : cCircleRadius;
141     circleRadius_ = Dimension(handleCircleRadius < 0 ? 0 : handleCircleRadius, DimensionUnit::VP);
142     double cStrokeWidth = patternLockComponent->GetStrokeWidth().ConvertToVp();
143     double handleStrokeWidth = cStrokeWidth > cSideLength / COL_COUNT ? cSideLength / COL_COUNT : cStrokeWidth;
144     strokeWidth_ = Dimension(handleStrokeWidth < 0 ? 0 : handleStrokeWidth, DimensionUnit::VP);
145     circleRadiusAnimatorToIncrease_ = circleRadius_;
146     circleRadiusAnimatorToDecrease_ = circleRadius_ * SCALE_ACTIVE_CIRCLE_RADIUS;
147     regularColor_ = patternLockComponent->GetRegularColor();
148     selectedColor_ = patternLockComponent->GetSelectedColor();
149     activeColor_ = patternLockComponent->GetActiveColor();
150     pathColor_ = patternLockComponent->GetPathColor();
151     autoReset_ = patternLockComponent->GetAutoReset();
152     callbackForJS_ = AceAsyncEvent<void(const std::shared_ptr<PatternCompleteEvent>&)>::Create(
153         patternLockComponent->GetPatternCompleteEvent(), context_);
154     patternLockController_ = patternLockComponent->GetPatternLockController();
155     if (patternLockController_) {
156         auto weak = AceType::WeakClaim(this);
157         patternLockController_->SetResetImpl([weak]() {
158             auto patternLock = weak.Upgrade();
159             if (patternLock) {
160                 patternLock->HandleReset();
161             }
162         });
163     }
164 }
Update(const RefPtr<Component> & component)165 void RenderPatternLock::Update(const RefPtr<Component>& component)
166 {
167     UpdateAttr(component);
168     // animator
169     if (!animator_) {
170         animator_ = CREATE_ANIMATOR(GetContext());
171         auto touchAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(0.0, 1.0, Curves::SHARP);
172         touchAnimation->AddListener([weak = AceType::WeakClaim(this)](double value) {
173             auto patternLock = weak.Upgrade();
174             if (patternLock) {
175                 Dimension radiusFir { patternLock->circleRadius_.ConvertToVp() *
176                                           (1 + (SCALE_SELECTED_CIRCLE_RADIUS - 1) * value),
177                     DimensionUnit::VP };
178                 patternLock->circleRadiusAnimatorToIncrease_ = radiusFir;
179                 double decreaseRate = 1 + (1 - value) * (SCALE_DECREASE - 1);
180                 Dimension radiusSec { patternLock->circleRadius_.ConvertToVp() * SCALE_ACTIVE_CIRCLE_RADIUS *
181                                           decreaseRate,
182                     DimensionUnit::VP };
183                 patternLock->circleRadiusAnimatorToDecrease_ = radiusSec;
184                 patternLock->MarkNeedRender();
185             }
186         });
187         animator_->ClearInterpolators();
188         animator_->AddInterpolator(touchAnimation);
189         animator_->SetFillMode(FillMode::FORWARDS);
190     }
191     MarkNeedLayout();
192 }
PerformLayout()193 void RenderPatternLock::PerformLayout()
194 {
195     Size layoutSizeAfterConstrain =
196         GetLayoutParam().Constrain(Size(NormalizeToPx(sideLength_), NormalizeToPx(sideLength_)));
197     SetLayoutSize(layoutSizeAfterConstrain);
198 }
AddChoosePoint(Offset offset,int16_t x,int16_t y)199 bool RenderPatternLock::AddChoosePoint(Offset offset, int16_t x, int16_t y)
200 {
201     const int16_t scale = RADIUS_TO_DIAMETER;
202     double offsetX = NormalizeToPx(sideLength_) / COL_COUNT / scale * (scale * x - 1);
203     double offsetY = NormalizeToPx(sideLength_) / COL_COUNT / scale * (scale * y - 1);
204     Offset centerOffset;
205     centerOffset.SetX(offsetX);
206     centerOffset.SetY(offsetY);
207     double distance = (offset - centerOffset).GetDistance();
208     if (distance <= (NormalizeToPx(circleRadius_) * SCALE_SELECTED_CIRCLE_RADIUS)) {
209         if (!CheckChoosePoint(x, y)) {
210             AddPassPoint(offset, x, y);
211             animator_->SetDuration(DOWN_DURATION);
212             animator_->Forward();
213             choosePoint_.push_back(PatternLockCell(x, y));
214         }
215         return true;
216     }
217     return false;
218 }
HandleReset()219 void RenderPatternLock::HandleReset()
220 {
221     isMoveEventValid_ = false;
222     choosePoint_.clear();
223     cellCenter_ = Offset::Zero();
224     MarkNeedRender();
225 }
GetCircleCenterByXY(const Offset & offset,int16_t x,int16_t y)226 Offset RenderPatternLock::GetCircleCenterByXY(const Offset& offset, int16_t x, int16_t y)
227 {
228     const int16_t scale = RADIUS_TO_DIAMETER;
229     Offset cellCenter;
230     cellCenter.SetX(offset.GetX() + NormalizeToPx(sideLength_) / COL_COUNT / scale * (x * scale - 1));
231     cellCenter.SetY(offset.GetY() + NormalizeToPx(sideLength_) / COL_COUNT / scale * (y * scale - 1));
232     return cellCenter;
233 }
CheckChoosePoint(int16_t x,int16_t y) const234 bool RenderPatternLock::CheckChoosePoint(int16_t x, int16_t y) const
235 {
236     for (auto it = choosePoint_.begin(); it != choosePoint_.end(); it++) {
237         if ((*it).GetColumn() == x && (*it).GetRow() == y) {
238             return true;
239         }
240     }
241     return false;
242 }
CheckChoosePointIsLastIndex(int16_t x,int16_t y,int16_t index) const243 bool RenderPatternLock::CheckChoosePointIsLastIndex(int16_t x, int16_t y, int16_t index) const
244 {
245     if (!choosePoint_.empty() && static_cast<int16_t>(choosePoint_.size()) >= index) {
246         if (choosePoint_.at(choosePoint_.size() - static_cast<uint32_t>(index)).GetColumn() == x &&
247             choosePoint_.at(choosePoint_.size() - static_cast<uint32_t>(index)).GetRow() == y) {
248             return true;
249         }
250     }
251     return false;
252 }
CheckAutoReset() const253 bool RenderPatternLock::CheckAutoReset() const
254 {
255     if (!autoReset_ && !choosePoint_.empty() && !isMoveEventValid_) {
256         return false;
257     }
258     return true;
259 }
AddPassPoint(Offset offset,int16_t x,int16_t y)260 void RenderPatternLock::AddPassPoint(Offset offset, int16_t x, int16_t y)
261 {
262     if (choosePoint_.empty()) {
263         return;
264     }
265     passPointCount_ = 0;
266     PatternLockCell lastCell = choosePoint_.back();
267     int16_t lastX = lastCell.GetColumn();
268     int16_t lastY = lastCell.GetRow();
269     int16_t lastCode = lastCell.GetCode();
270     int16_t nowCode = COL_COUNT * (y - 1) + (x - 1);
271     std::vector<PatternLockCell> passPointVec;
272     for (int16_t i = 1; i <= COL_COUNT; i++) {
273         for (int16_t j = 1; j <= COL_COUNT; j++) {
274             PatternLockCell passPoint = PatternLockCell(i, j);
275             if ((passPoint.GetCode() >= nowCode && passPoint.GetCode() >= lastCode) ||
276                 (passPoint.GetCode() <= nowCode && passPoint.GetCode() <= lastCode)) {
277                 continue;
278             }
279             if ((j != y) && (j != lastY) &&
280                 ((double(lastX - i) / (lastY - j) == double(i - x) / (j - y)) && !CheckChoosePoint(i, j))) {
281                 passPointVec.push_back(passPoint);
282             }
283             if ((j == lastY) && (j == y) && !CheckChoosePoint(i, j)) {
284                 passPointVec.push_back(passPoint);
285             }
286         }
287     }
288     size_t passPointLength = passPointVec.size();
289     if (passPointLength == 0) {
290         return;
291     }
292     passPointCount_ = static_cast<int16_t>(passPointLength);
293     if (nowCode > lastCode) {
294         choosePoint_.push_back(passPointVec.front());
295         if (passPointLength > 1) {
296             choosePoint_.push_back(passPointVec.back());
297         }
298     } else {
299         choosePoint_.push_back(passPointVec.back());
300         if (passPointLength > 1) {
301             choosePoint_.push_back(passPointVec.front());
302         }
303     }
304 }
305 } // namespace OHOS::Ace::V2
306