1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.view;
18 
19 import static android.util.MathUtils.constrain;
20 
21 import static java.lang.Math.max;
22 import static java.lang.Math.min;
23 
24 import android.annotation.NonNull;
25 import android.graphics.Rect;
26 import android.webkit.WebView;
27 
28 /**
29  * ScrollCapture for WebView.
30  */
31 class WebViewCaptureHelper implements ScrollCaptureViewHelper<WebView> {
32     private static final String TAG = "WebViewScrollCapture";
33 
34     private final Rect mRequestWebViewLocal = new Rect();
35     private final Rect mWebViewBounds = new Rect();
36 
37     private int mOriginScrollY;
38     private int mOriginScrollX;
39 
40     @Override
onAcceptSession(@onNull WebView view)41     public boolean onAcceptSession(@NonNull WebView view) {
42         return view.isVisibleToUser()
43                 && (view.getContentHeight() * view.getScale()) > view.getHeight();
44     }
45 
46     @Override
onPrepareForStart(@onNull WebView view, @NonNull Rect scrollBounds)47     public void onPrepareForStart(@NonNull WebView view, @NonNull Rect scrollBounds) {
48         mOriginScrollX = view.getScrollX();
49         mOriginScrollY = view.getScrollY();
50     }
51 
52     @NonNull
53     @Override
onScrollRequested(@onNull WebView view, @NonNull Rect scrollBounds, @NonNull Rect requestRect)54     public ScrollResult onScrollRequested(@NonNull WebView view, @NonNull Rect scrollBounds,
55             @NonNull Rect requestRect) {
56 
57         int scrollDelta = view.getScrollY() - mOriginScrollY;
58 
59         ScrollResult result = new ScrollResult();
60         result.requestedArea = new Rect(requestRect);
61         result.availableArea = new Rect();
62         result.scrollDelta = scrollDelta;
63 
64         mWebViewBounds.set(0, 0, view.getWidth(), view.getHeight());
65 
66         if (!view.isVisibleToUser()) {
67             return result;
68         }
69 
70         // Map the request into local coordinates
71         mRequestWebViewLocal.set(requestRect);
72         mRequestWebViewLocal.offset(0, -scrollDelta);
73 
74         // Offset to center the rect vertically, clamp to available content
75         int upLimit = min(0, -view.getScrollY());
76         int contentHeightPx = (int) (view.getContentHeight() * view.getScale());
77         int downLimit = max(0, (contentHeightPx - view.getHeight()) - view.getScrollY());
78         int scrollToCenter = mRequestWebViewLocal.centerY() - mWebViewBounds.centerY();
79         int scrollMovement = constrain(scrollToCenter, upLimit, downLimit);
80 
81         // Scroll and update relative based on  the new position
82         view.scrollBy(mOriginScrollX, scrollMovement);
83         scrollDelta = view.getScrollY() - mOriginScrollY;
84         mRequestWebViewLocal.offset(0, -scrollMovement);
85         result.scrollDelta = scrollDelta;
86 
87         if (mRequestWebViewLocal.intersect(mWebViewBounds)) {
88             result.availableArea = new Rect(mRequestWebViewLocal);
89             result.availableArea.offset(0, result.scrollDelta);
90         }
91         return result;
92     }
93 
94     @Override
onPrepareForEnd(@onNull WebView view)95     public void onPrepareForEnd(@NonNull WebView view) {
96         view.scrollTo(mOriginScrollX, mOriginScrollY);
97     }
98 
99 }
100 
101