1 /*
2  * Copyright (c) 2023 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 #include "core/components_ng/pattern/folder_stack/folder_stack_pattern.h"
16 
17 #include "base/log/dump_log.h"
18 #include "base/memory/ace_type.h"
19 #include "base/utils/utils.h"
20 #include "core/common/container.h"
21 #include "core/common/display_info.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/layout/layout_property.h"
25 #include "core/components_ng/pattern/folder_stack/control_parts_stack_node.h"
26 #include "core/components_ng/pattern/folder_stack/folder_stack_event_hub.h"
27 #include "core/components_ng/pattern/folder_stack/folder_stack_group_node.h"
28 #include "core/components_ng/pattern/folder_stack/folder_stack_layout_algorithm.h"
29 #include "core/components_ng/pattern/folder_stack/folder_stack_layout_property.h"
30 #include "core/components_ng/pattern/folder_stack/folder_stack_pattern.h"
31 #include "core/components_ng/pattern/folder_stack/hover_stack_node.h"
32 #include "core/components_ng/property/property.h"
33 #include "core/components_ng/render/paint_property.h"
34 #include "core/pipeline/pipeline_base.h"
35 #include "core/pipeline_ng/pipeline_context.h"
36 
37 namespace OHOS::Ace::NG {
38 namespace {
39 constexpr int32_t ANIMATION_TIME = 400;
40 constexpr int32_t DELAY_TIME = 300;
41 const RefPtr<Curve> FOLDER_STACK_ANIMATION_CURVE =
42     AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 328.0f, 36.0f);
43 } // namespace
44 
OnAttachToFrameNode()45 void FolderStackPattern::OnAttachToFrameNode()
46 {
47     Pattern::OnAttachToFrameNode();
48     auto pipeline = PipelineContext::GetCurrentContext();
49     CHECK_NULL_VOID(pipeline);
50     auto callbackId = pipeline->RegisterFoldStatusChangedCallback([weak = WeakClaim(this)](FoldStatus folderStatus) {
51         auto pattern = weak.Upgrade();
52         if (pattern) {
53             pattern->RefreshStack(folderStatus);
54         }
55     });
56     UpdateFoldStatusChangedCallbackId(callbackId);
57 }
58 
OnDetachFromFrameNode(FrameNode * node)59 void FolderStackPattern::OnDetachFromFrameNode(FrameNode* node)
60 {
61     auto pipeline = PipelineContext::GetCurrentContext();
62     CHECK_NULL_VOID(pipeline);
63     if (HasFoldStatusChangedCallbackId()) {
64         pipeline->UnRegisterFoldStatusChangedCallback(foldStatusChangedCallbackId_.value_or(-1));
65     }
66     Pattern::OnDetachFromFrameNode(node);
67 }
68 
OnModifyDone()69 void FolderStackPattern::OnModifyDone()
70 {
71     Pattern::OnModifyDone();
72     InitFolderStackPatternAppearCallback();
73 }
74 
InitFolderStackPatternAppearCallback()75 void FolderStackPattern::InitFolderStackPatternAppearCallback()
76 {
77     auto frameNode = GetHost();
78     CHECK_NULL_VOID(frameNode);
79     if (isAppearCallback_) {
80         return;
81     }
82     auto eventHub = frameNode->GetEventHub<EventHub>();
83     CHECK_NULL_VOID(eventHub);
84     auto onDisappear = [weak = WeakClaim(this)]() {
85         auto folderStackPattern = weak.Upgrade();
86         CHECK_NULL_VOID(folderStackPattern);
87         folderStackPattern->RestoreScreenState();
88     };
89     eventHub->SetOnDisappear(std::move(onDisappear));
90     isAppearCallback_ = true;
91 }
92 
DumpInfo()93 void FolderStackPattern::DumpInfo()
94 {
95     CHECK_NULL_VOID(displayInfo_);
96     auto rotation = displayInfo_->GetRotation();
97     DumpLog::GetInstance().AddDesc(std::string("rotation: ").append(std::to_string(static_cast<int32_t>(rotation))));
98 }
99 
SetLayoutBeforeAnimation(const RefPtr<FolderStackGroupNode> & hostNode)100 void FolderStackPattern::SetLayoutBeforeAnimation(const RefPtr<FolderStackGroupNode>& hostNode)
101 {
102     auto controlPartsStackNode = hostNode->GetControlPartsStackNode();
103     auto index = hostNode->GetChildIndexById(controlPartsStackNode->GetId());
104     auto controlPartsStackWrapper = hostNode->GetOrCreateChildByIndex(index);
105     CHECK_NULL_VOID(controlPartsStackWrapper);
106     auto controlPartsgeometryNode = controlPartsStackWrapper->GetGeometryNode();
107     auto controlPartsOffset = controlPartsgeometryNode->GetMarginFrameOffset();
108     auto controlPartsChildNodeList = controlPartsStackWrapper->GetAllChildrenWithBuild();
109     for (auto& controlPartsChildNode : controlPartsChildNodeList) {
110         auto controlPartsChildGeometryNode = controlPartsChildNode->GetGeometryNode();
111         auto controlPartsChildOffset = OffsetT<float>(controlPartsChildGeometryNode->GetMarginFrameOffset().GetX(),
112             controlPartsOffset.GetY() + controlPartsChildGeometryNode->GetMarginFrameOffset().GetY());
113         controlPartsChildGeometryNode->SetMarginFrameOffset(controlPartsChildOffset);
114         auto controlPartsChildFrameNode = controlPartsChildNode->GetHostNode();
115         if (!controlPartsChildFrameNode) {
116             continue;
117         }
118         auto renderContext = controlPartsChildFrameNode->GetRenderContext();
119         CHECK_NULL_VOID(renderContext);
120         renderContext->SyncGeometryProperties(AceType::RawPtr(controlPartsChildGeometryNode));
121     }
122     auto hoverStackOffset = OffsetT<float>(0.0f, 0.0f);
123     controlPartsgeometryNode->SetMarginFrameOffset(hoverStackOffset);
124     auto ControlPartsFrameNode = AceType::DynamicCast<FrameNode>(controlPartsStackNode);
125     CHECK_NULL_VOID(ControlPartsFrameNode);
126     auto renderContext = ControlPartsFrameNode->GetRenderContext();
127     CHECK_NULL_VOID(renderContext);
128     renderContext->SyncGeometryProperties(AceType::RawPtr(controlPartsgeometryNode));
129 }
130 
RefreshStack(FoldStatus foldStatus)131 void FolderStackPattern::RefreshStack(FoldStatus foldStatus)
132 {
133     TAG_LOGD(AceLogTag::ACE_FOLDER_STACK, "the current folding state is:%{public}d", foldStatus);
134     currentFoldStatus_ = foldStatus;
135     if (foldStatusDelayTask_) {
136         foldStatusDelayTask_.Cancel();
137     }
138     auto pipeline = PipelineContext::GetCurrentContext();
139     CHECK_NULL_VOID(pipeline);
140     auto taskExecutor = pipeline->GetTaskExecutor();
141     CHECK_NULL_VOID(taskExecutor);
142     foldStatusDelayTask_.Reset([weak = WeakClaim(this), currentFoldStatus = currentFoldStatus_,
143         lastFoldStatus = lastFoldStatus_]() {
144         auto pattern = weak.Upgrade();
145         CHECK_NULL_VOID(pattern);
146         auto container = Container::Current();
147         CHECK_NULL_VOID(container);
148         auto pipeline = DynamicCast<PipelineContext>(container->GetPipelineContext());
149         CHECK_NULL_VOID(pipeline);
150         auto displayInfo = container->GetDisplayInfo();
151         if (displayInfo->GetFoldStatus() != FoldStatus::HALF_FOLD) {
152             pattern->RestoreScreenState();
153         } else {
154             pattern->SetAutoRotate();
155         }
156         auto windowManager = pipeline->GetWindowManager();
157         auto windowMode = windowManager->GetWindowMode();
158         auto rotation = displayInfo->GetRotation();
159         auto isLandscape = rotation == Rotation::ROTATION_90 || rotation == Rotation::ROTATION_270;
160         if (currentFoldStatus == displayInfo->GetFoldStatus() && isLandscape &&
161             windowMode == WindowMode::WINDOW_MODE_FULLSCREEN) {
162             auto host = pattern->GetHost();
163             CHECK_NULL_VOID(host);
164             auto hostNode = AceType::DynamicCast<FolderStackGroupNode>(host);
165             CHECK_NULL_VOID(hostNode);
166             if (currentFoldStatus == FoldStatus::EXPAND && lastFoldStatus == FoldStatus::HALF_FOLD) {
167                 pattern->SetLayoutBeforeAnimation(hostNode);
168             }
169             pattern->OnFolderStateChangeSend(currentFoldStatus);
170             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
171         }
172         pattern->hasFoldStatusDelayTask_ = false;
173     });
174     lastFoldStatus_ = currentFoldStatus_;
175     TAG_LOGD(AceLogTag::ACE_FOLDER_STACK, "the last folding state was:%{public}d", lastFoldStatus_);
176     hasFoldStatusDelayTask_ = true;
177     taskExecutor->PostDelayedTask(
178         foldStatusDelayTask_, TaskExecutor::TaskType::UI, DELAY_TIME, "ArkUIFolderStackStatusChange");
179 }
180 
OnFolderStateChangeSend(FoldStatus foldStatus)181 void FolderStackPattern::OnFolderStateChangeSend(FoldStatus foldStatus)
182 {
183     FolderEventInfo event(foldStatus);
184     auto eventHub = GetEventHub<FolderStackEventHub>();
185     if (eventHub) {
186         eventHub->OnFolderStateChange(event);
187     }
188 }
189 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,bool skipMeasure,bool skipLayout)190 bool FolderStackPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, bool skipMeasure, bool skipLayout)
191 {
192     if (skipMeasure && skipLayout) {
193         return false;
194     }
195     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
196     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
197     auto folderStackLayoutAlgorithm =
198         DynamicCast<FolderStackLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
199     CHECK_NULL_RETURN(folderStackLayoutAlgorithm, false);
200     auto isIntoFolderStack = folderStackLayoutAlgorithm->GetIsIntoFolderStack();
201     if (isIntoFolderStack != hasInHoverMode_) {
202         StartOffsetEnteringAnimation();
203     }
204     hasInHoverMode_ = isIntoFolderStack;
205     return false;
206 }
207 
StartOffsetEnteringAnimation()208 void FolderStackPattern::StartOffsetEnteringAnimation()
209 {
210     auto host = GetHost();
211     if (!host->GetLayoutProperty<FolderStackLayoutProperty>()->GetEnableAnimation().value_or(true)) {
212         return;
213     }
214     AnimationOption optionPosition;
215     optionPosition.SetDuration(ANIMATION_TIME);
216     optionPosition.SetCurve(FOLDER_STACK_ANIMATION_CURVE);
217     auto renderContext = GetRenderContext();
218     auto pipeline = host->GetContext();
219     CHECK_NULL_VOID(pipeline);
220     auto pageNode = pipeline->GetStageManager()->GetLastPage();
221     auto pageHeight = pageNode->GetGeometryNode()->GetFrameSize().Height();
222     TranslateOptions rawTranslate = TranslateOptions(0.0f, pageHeight, 0.0f);
223     renderContext->OnTransformTranslateUpdate(rawTranslate);
224     TranslateOptions targetTranslate = TranslateOptions(0.0f, 0.0f, 0.0f);
225     AnimationUtils::Animate(optionPosition, [&]() { renderContext->OnTransformTranslateUpdate(targetTranslate); });
226 }
227 
GetRenderContext()228 RefPtr<RenderContext> FolderStackPattern::GetRenderContext()
229 {
230     auto frameNode = GetHost();
231     CHECK_NULL_RETURN(frameNode, nullptr);
232     return frameNode->GetRenderContext();
233 }
234 
BeforeCreateLayoutWrapper()235 void FolderStackPattern::BeforeCreateLayoutWrapper()
236 {
237     Pattern::BeforeCreateLayoutWrapper();
238     UpdateChildAlignment();
239     SetAutoRotate();
240 }
241 
SetAutoRotate()242 void FolderStackPattern::SetAutoRotate()
243 {
244     auto layoutProperty = GetLayoutProperty<FolderStackLayoutProperty>();
245     auto autoHalfFold = layoutProperty->GetAutoHalfFold().value_or(true);
246     auto container = Container::Current();
247     CHECK_NULL_VOID(container);
248     auto displayInfo = container->GetDisplayInfo();
249     CHECK_NULL_VOID(displayInfo);
250     displayInfo_ = displayInfo;
251     auto foldStatus = displayInfo->GetFoldStatus();
252     auto orientation = container->GetOrientation();
253     TAG_LOGI(AceLogTag::ACE_FOLDER_STACK,
254         "the autoHalfFold state is:%{public}d, direction of rotation is:%{public}d",
255         autoHalfFold, orientation);
256     if (autoHalfFold && foldStatus == FoldStatus::HALF_FOLD && orientation != Orientation::SENSOR) {
257         container->SetOrientation(Orientation::SENSOR);
258         isScreenRotationLocked_ = true;
259         lastOrientation_ = orientation;
260         isNeedRestoreScreenState_ = true;
261     }
262 }
263 
OnVisibleChange(bool isVisible)264 void FolderStackPattern::OnVisibleChange(bool isVisible)
265 {
266     Pattern::OnVisibleChange(isVisible);
267     if (!isVisible) {
268         RestoreScreenState();
269     }
270 }
271 
RestoreScreenState()272 void FolderStackPattern::RestoreScreenState()
273 {
274     if (isNeedRestoreScreenState_) {
275         isNeedRestoreScreenState_ = false;
276         auto container = Container::Current();
277         CHECK_NULL_VOID(container);
278         TAG_LOGD(AceLogTag::ACE_FOLDER_STACK, "set orientation to lastOrientation:%{public}d", lastOrientation_);
279         container->SetOrientation(lastOrientation_);
280     }
281 }
282 
UpdateChildAlignment()283 void FolderStackPattern::UpdateChildAlignment()
284 {
285     auto hostNode = AceType::DynamicCast<FolderStackGroupNode>(GetHost());
286     CHECK_NULL_VOID(hostNode);
287     auto folderStackLayoutProperty = GetLayoutProperty<FolderStackLayoutProperty>();
288     CHECK_NULL_VOID(folderStackLayoutProperty);
289     auto align = Alignment::CENTER;
290     if (folderStackLayoutProperty->GetPositionProperty()) {
291         align = folderStackLayoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER);
292     }
293     auto controlPartsStackNode = AceType::DynamicCast<ControlPartsStackNode>(hostNode->GetControlPartsStackNode());
294     if (controlPartsStackNode) {
295         auto controlPartsLayoutProperty =
296             AceType::DynamicCast<LayoutProperty>(controlPartsStackNode->GetLayoutProperty());
297         controlPartsLayoutProperty->UpdateAlignment(align);
298     }
299     auto hoverStackNode = AceType::DynamicCast<HoverStackNode>(hostNode->GetHoverNode());
300     if (hoverStackNode) {
301         auto hoverLayoutProperty = AceType::DynamicCast<LayoutProperty>(hoverStackNode->GetLayoutProperty());
302         hoverLayoutProperty->UpdateAlignment(align);
303     }
304 }
305 
DumpSimplifyInfo(std::unique_ptr<JsonValue> & json)306 void FolderStackPattern::DumpSimplifyInfo(std::unique_ptr<JsonValue>& json)
307 {
308     CHECK_NULL_VOID(displayInfo_);
309     auto rotation = displayInfo_->GetRotation();
310     json->Put("Rotation", static_cast<int32_t>(rotation));
311 }
312 } // namespace OHOS::Ace::NG