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 "safe_area_manager.h"
16 
17 #include "base/utils/utils.h"
18 #include "core/components/container_modal/container_modal_constants.h"
19 #include "core/components_ng/pattern/navrouter/navdestination_pattern.h"
20 #include "core/components_ng/pattern/stage/page_pattern.h"
21 #include "core/components_ng/property/safe_area_insets.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 
24 namespace OHOS::Ace::NG {
GenerateCutOutAreaWithRoot(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)25 SafeAreaInsets GenerateCutOutAreaWithRoot(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
26 {
27     auto pipeline = PipelineContext::GetCurrentContext();
28     CHECK_NULL_RETURN(pipeline, {});
29     CHECK_NULL_RETURN(pipeline->GetUseCutout(), {});
30     // cutout regions adjacent to edges.
31     auto cutoutArea = safeArea;
32 
33     if (cutoutArea.top_.IsValid()) {
34         cutoutArea.top_.start = 0;
35     }
36     if (safeArea.bottom_.IsValid()) {
37         cutoutArea.bottom_.end = rootSize.Height().has_value() ? rootSize.Height().value()
38                                                                : PipelineContext::GetCurrentRootHeight();
39     }
40     if (cutoutArea.left_.IsValid()) {
41         cutoutArea.left_.start = 0;
42     }
43     if (cutoutArea.right_.IsValid()) {
44         cutoutArea.right_.end = rootSize.Width().has_value() ? rootSize.Width().value()
45                                                              : PipelineContext::GetCurrentRootWidth();
46     }
47     return cutoutArea;
48 }
49 
CheckCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)50 bool SafeAreaManager::CheckCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
51 {
52     return cutoutSafeArea_ != GenerateCutOutAreaWithRoot(safeArea, rootSize);
53 }
54 
UpdateCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)55 bool SafeAreaManager::UpdateCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
56 {
57     auto safeAreaWithRoot = GenerateCutOutAreaWithRoot(safeArea, rootSize);
58     if (cutoutSafeArea_ == safeAreaWithRoot) {
59         return false;
60     }
61     ACE_SCOPED_TRACE("SafeAreaManager::UpdateCutoutSafeArea %s", safeAreaWithRoot.ToString().c_str());
62     cutoutSafeArea_ = safeAreaWithRoot;
63     return true;
64 }
65 
CheckSystemSafeArea(const SafeAreaInsets & safeArea)66 bool SafeAreaManager::CheckSystemSafeArea(const SafeAreaInsets& safeArea)
67 {
68     return systemSafeArea_ != safeArea;
69 }
70 
UpdateSystemSafeArea(const SafeAreaInsets & safeArea)71 bool SafeAreaManager::UpdateSystemSafeArea(const SafeAreaInsets& safeArea)
72 {
73     if (systemSafeArea_ == safeArea) {
74         return false;
75     }
76     ACE_SCOPED_TRACE("SafeAreaManager::UpdateSystemSafeArea %s", safeArea.ToString().c_str());
77     systemSafeArea_ = safeArea;
78     return true;
79 }
80 
CheckNavArea(const SafeAreaInsets & safeArea)81 bool SafeAreaManager::CheckNavArea(const SafeAreaInsets& safeArea)
82 {
83     return navSafeArea_ != safeArea;
84 }
85 
UpdateNavArea(const SafeAreaInsets & safeArea)86 bool SafeAreaManager::UpdateNavArea(const SafeAreaInsets& safeArea)
87 {
88     if (navSafeArea_ == safeArea) {
89         return false;
90     }
91     ACE_SCOPED_TRACE("SafeAreaManager::UpdateNavArea %s", safeArea.ToString().c_str());
92     navSafeArea_ = safeArea;
93     return true;
94 }
95 
UpdateKeyboardSafeArea(float keyboardHeight,std::optional<uint32_t> rootHeight)96 bool SafeAreaManager::UpdateKeyboardSafeArea(float keyboardHeight, std::optional<uint32_t> rootHeight)
97 {
98     uint32_t bottom;
99     if (systemSafeArea_.bottom_.IsValid()) {
100         bottom = systemSafeArea_.bottom_.start;
101     } else {
102         bottom = rootHeight.has_value() ? rootHeight.value() : PipelineContext::GetCurrentRootHeight();
103     }
104     SafeAreaInsets::Inset inset = { .start = bottom - keyboardHeight, .end = bottom };
105     if (inset == keyboardInset_) {
106         return false;
107     }
108     keyboardInset_ = inset;
109     return true;
110 }
111 
GetCombinedSafeArea(const SafeAreaExpandOpts & opts) const112 SafeAreaInsets SafeAreaManager::GetCombinedSafeArea(const SafeAreaExpandOpts& opts) const
113 {
114     SafeAreaInsets res;
115     if (!IsSafeAreaValid()) {
116         return {};
117     }
118     if (opts.type & SAFE_AREA_TYPE_CUTOUT) {
119         res = res.Combine(cutoutSafeArea_);
120     }
121     if (opts.type & SAFE_AREA_TYPE_SYSTEM) {
122         res = res.Combine(systemSafeArea_).Combine(navSafeArea_);
123     }
124     if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
125         return res;
126     }
127     if ((keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
128         keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET) && (opts.type & SAFE_AREA_TYPE_KEYBOARD)) {
129         res.bottom_ = res.bottom_.Combine(keyboardInset_);
130     }
131     return res;
132 }
133 
IsSafeAreaValid() const134 bool SafeAreaManager::IsSafeAreaValid() const
135 {
136 #ifdef PREVIEW
137     return !ignoreSafeArea_;
138 #else
139     return !(ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_));
140 #endif
141 }
142 
SetIsFullScreen(bool value)143 bool SafeAreaManager::SetIsFullScreen(bool value)
144 {
145     if (isFullScreen_ == value) {
146         return false;
147     }
148     isFullScreen_ = value;
149     LOGI("SafeAreaManager::SetIsFullScreen %{public}d", isFullScreen_);
150     return true;
151 }
152 
SetIsNeedAvoidWindow(bool value)153 bool SafeAreaManager::SetIsNeedAvoidWindow(bool value)
154 {
155     if (isNeedAvoidWindow_ == value) {
156         return false;
157     }
158     isNeedAvoidWindow_ = value;
159     LOGI("SafeAreaManager::SetIsNeedAvoidWindow %{public}d", isNeedAvoidWindow_);
160     return true;
161 }
162 
SetIgnoreSafeArea(bool value)163 bool SafeAreaManager::SetIgnoreSafeArea(bool value)
164 {
165     if (ignoreSafeArea_ == value) {
166         return false;
167     }
168     ignoreSafeArea_ = value;
169     LOGI("SafeAreaManager::SetIgnoreSafeArea %{public}d", ignoreSafeArea_);
170     return true;
171 }
172 
SetKeyBoardAvoidMode(KeyBoardAvoidMode value)173 bool SafeAreaManager::SetKeyBoardAvoidMode(KeyBoardAvoidMode value)
174 {
175     if (keyboardAvoidMode_ == value) {
176         return false;
177     }
178     if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE || value == KeyBoardAvoidMode::NONE) {
179         keyboardOffset_ = 0.0f;
180     }
181     keyboardAvoidMode_ = value;
182     keyboardSafeAreaEnabled_ = keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE
183         || keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET;
184     TAG_LOGI(ACE_LAYOUT, "SetKeyBoardAvoidMode %{public}d", keyboardAvoidMode_);
185     return true;
186 }
187 
GetKeyBoardAvoidMode()188 KeyBoardAvoidMode SafeAreaManager::GetKeyBoardAvoidMode()
189 {
190     return keyboardAvoidMode_;
191 }
192 
SetIsAtomicService(bool value)193 bool SafeAreaManager::SetIsAtomicService(bool value)
194 {
195     if (isAtomicService_ == value) {
196         return false;
197     }
198     isAtomicService_ = value;
199     LOGI("SafeAreaManager::SetIsAtomicService %{public}d", isAtomicService_);
200     return true;
201 }
202 
IsAtomicService() const203 bool SafeAreaManager::IsAtomicService() const
204 {
205     return isAtomicService_;
206 }
207 
GetSystemSafeArea() const208 SafeAreaInsets SafeAreaManager::GetSystemSafeArea() const
209 {
210     return systemSafeArea_;
211 }
212 
GetCutoutSafeArea() const213 SafeAreaInsets SafeAreaManager::GetCutoutSafeArea() const
214 {
215     if (!IsSafeAreaValid()) {
216         return {};
217     }
218     return cutoutSafeArea_;
219 }
220 
GetSafeArea() const221 SafeAreaInsets SafeAreaManager::GetSafeArea() const
222 {
223     if (!IsSafeAreaValid()) {
224         return {};
225     }
226     return systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
227 }
228 
GetSafeAreaWithoutCutout() const229 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutCutout() const
230 {
231     if (!IsSafeAreaValid()) {
232         return {};
233     }
234     return systemSafeArea_.Combine(navSafeArea_);
235 }
236 
GetSafeAreaWithoutProcess() const237 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutProcess() const
238 {
239     return systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
240 }
241 
SafeAreaToPadding(bool withoutProcess)242 PaddingPropertyF SafeAreaManager::SafeAreaToPadding(bool withoutProcess)
243 {
244     if (!withoutProcess) {
245 #ifdef PREVIEW
246         if (ignoreSafeArea_) {
247             return {};
248         }
249 #else
250         if (ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_)) {
251             return {};
252         }
253 #endif
254     }
255     auto combinedSafeArea = systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
256     PaddingPropertyF result;
257     if (combinedSafeArea.left_.IsValid()) {
258         result.left = combinedSafeArea.left_.Length();
259     }
260     if (combinedSafeArea.top_.IsValid()) {
261         result.top = combinedSafeArea.top_.Length();
262     }
263     if (combinedSafeArea.right_.IsValid()) {
264         result.right = combinedSafeArea.right_.Length();
265     }
266     if (combinedSafeArea.bottom_.IsValid()) {
267         result.bottom = combinedSafeArea.bottom_.Length();
268     }
269     return result;
270 }
271 
GetKeyboardOffset(bool withoutProcess) const272 float SafeAreaManager::GetKeyboardOffset(bool withoutProcess) const
273 {
274     if (withoutProcess) {
275         return keyboardOffset_;
276     }
277     if (keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
278         keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET ||
279         keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
280         return 0.0f;
281     }
282     return keyboardOffset_;
283 }
284 
GetWindowWrapperOffset()285 OffsetF SafeAreaManager::GetWindowWrapperOffset()
286 {
287     auto pipelineContext = PipelineContext::GetCurrentContext();
288     CHECK_NULL_RETURN(pipelineContext, OffsetF());
289     auto windowManager = pipelineContext->GetWindowManager();
290     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
291                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
292     if (isContainerModal) {
293         auto wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
294             static_cast<float>((pipelineContext->GetCustomTitleHeight() + CONTAINER_BORDER_WIDTH).ConvertToPx()));
295         return wrapperOffset;
296     }
297     return OffsetF();
298 }
299 
ExpandSafeArea()300 void SafeAreaManager::ExpandSafeArea()
301 {
302     ACE_SCOPED_TRACE("ExpandSafeArea node count %zu, IsSafeAreaValid: %d, ignoreSafeArea: %d, isFullScreen: %d, "
303                      "isNeedAvoidWindow %d",
304         needExpandNodes_.size(), IsSafeAreaValid(), ignoreSafeArea_, isFullScreen_, isNeedAvoidWindow_);
305     auto pipeline = PipelineContext::GetCurrentContext();
306     CHECK_NULL_VOID(pipeline);
307     auto manager = pipeline->GetSafeAreaManager();
308     auto iter = needExpandNodes_.begin();
309     while (iter != needExpandNodes_.end()) {
310         auto frameNode = (*iter).Upgrade();
311         if (frameNode) {
312             manager->AddGeoRestoreNode(frameNode);
313             frameNode->ExpandSafeArea();
314         }
315         ++iter;
316     }
317     ClearNeedExpandNode();
318 }
319 
AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode> & node)320 bool SafeAreaManager::AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode>& node)
321 {
322     if (needExpandNodes_.find(node) == needExpandNodes_.end()) {
323         AddNeedExpandNode(node);
324         return true;
325     }
326     return false;
327 }
328 
CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode> & frameNode)329 bool SafeAreaManager::CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode>& frameNode)
330 {
331     if (frameNode->GetTag() != V2::PAGE_ETS_TAG) {
332         return false;
333     }
334     // page will not avoid keyboard when lastChild is sheet
335     RefPtr<OverlayManager> overlay;
336     if (frameNode->RootNodeIsPage()) {
337         auto pattern = frameNode->GetPattern<PagePattern>();
338         CHECK_NULL_RETURN(pattern, true);
339         overlay = pattern->GetOverlayManager();
340     } else {
341         auto navNode = FrameNode::GetFrameNode(V2::NAVDESTINATION_VIEW_ETS_TAG, frameNode->GetRootNodeId());
342         CHECK_NULL_RETURN(navNode, true);
343         auto pattern = navNode->GetPattern<NavDestinationPattern>();
344         CHECK_NULL_RETURN(pattern, true);
345         overlay = pattern->GetOverlayManager();
346     }
347     CHECK_NULL_RETURN(overlay, true);
348     return overlay->CheckPageNeedAvoidKeyboard();
349 }
350 } // namespace OHOS::Ace::NG
351