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.os.CancellationSignal;
27 import android.webkit.WebView;
28 
29 import java.util.function.Consumer;
30 
31 /**
32  * ScrollCapture for WebView.
33  */
34 public class WebViewCaptureHelper implements ScrollCaptureViewHelper<WebView> {
35     private static final String TAG = "WebViewScrollCapture";
36 
37     private final Rect mRequestWebViewLocal = new Rect();
38     private final Rect mWebViewBounds = new Rect();
39 
40     private int mOriginScrollY;
41     private int mOriginScrollX;
42 
43     @Override
onAcceptSession(@onNull WebView view)44     public boolean onAcceptSession(@NonNull WebView view) {
45         return view.isVisibleToUser()
46                 && (view.getContentHeight() * view.getScale()) > view.getHeight();
47     }
48 
49     @Override
onPrepareForStart(@onNull WebView view, @NonNull Rect scrollBounds)50     public void onPrepareForStart(@NonNull WebView view, @NonNull Rect scrollBounds) {
51         mOriginScrollX = view.getScrollX();
52         mOriginScrollY = view.getScrollY();
53     }
54 
55     @NonNull
56     @Override
onScrollRequested(@onNull WebView view, @NonNull Rect scrollBounds, @NonNull Rect requestRect, CancellationSignal cancellationSignal, Consumer<ScrollResult> resultConsumer)57     public void onScrollRequested(@NonNull WebView view, @NonNull Rect scrollBounds,
58             @NonNull Rect requestRect, CancellationSignal cancellationSignal,
59             Consumer<ScrollResult> resultConsumer) {
60 
61         int scrollDelta = view.getScrollY() - mOriginScrollY;
62 
63         ScrollResult result = new ScrollResult();
64         result.requestedArea = new Rect(requestRect);
65         result.availableArea = new Rect();
66         result.scrollDelta = scrollDelta;
67 
68         mWebViewBounds.set(0, 0, view.getWidth(), view.getHeight());
69 
70         if (!view.isVisibleToUser()) {
71             resultConsumer.accept(result);
72         }
73 
74         // Map the request into local coordinates
75         mRequestWebViewLocal.set(requestRect);
76         mRequestWebViewLocal.offset(0, -scrollDelta);
77 
78         // Offset to center the rect vertically, clamp to available content
79         int upLimit = min(0, -view.getScrollY());
80         int contentHeightPx = (int) (view.getContentHeight() * view.getScale());
81         int downLimit = max(0, (contentHeightPx - view.getHeight()) - view.getScrollY());
82         int scrollToCenter = mRequestWebViewLocal.centerY() - mWebViewBounds.centerY();
83         int scrollMovement = constrain(scrollToCenter, upLimit, downLimit);
84 
85         // Scroll and update relative based on  the new position
86         view.scrollBy(mOriginScrollX, scrollMovement);
87         scrollDelta = view.getScrollY() - mOriginScrollY;
88         mRequestWebViewLocal.offset(0, -scrollMovement);
89         result.scrollDelta = scrollDelta;
90 
91         if (mRequestWebViewLocal.intersect(mWebViewBounds)) {
92             result.availableArea = new Rect(mRequestWebViewLocal);
93             result.availableArea.offset(0, result.scrollDelta);
94         }
95         resultConsumer.accept(result);
96     }
97 
98     @Override
onPrepareForEnd(@onNull WebView view)99     public void onPrepareForEnd(@NonNull WebView view) {
100         view.scrollTo(mOriginScrollX, mOriginScrollY);
101     }
102 
103 }
104 
105