1 /*
2  * Copyright (C) 2020 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 android.service.autofill;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SuppressLint;
21 import android.content.Context;
22 import android.os.RemoteException;
23 import android.util.Log;
24 import android.util.MathUtils;
25 import android.view.MotionEvent;
26 import android.view.ViewConfiguration;
27 import android.widget.FrameLayout;
28 
29 /**
30  * This class is the root view for an inline suggestion. It is responsible for
31  * detecting the click on the item and to also transfer input focus to the IME
32  * window if we detect the user is scrolling.
33  *
34  * @hide
35  */
36 @SuppressLint("ViewConstructor")
37 public class InlineSuggestionRoot extends FrameLayout {
38     private static final String TAG = "InlineSuggestionRoot";
39 
40     private final @NonNull IInlineSuggestionUiCallback mCallback;
41     private final int mTouchSlop;
42 
43     private float mDownX;
44     private float mDownY;
45 
InlineSuggestionRoot(@onNull Context context, @NonNull IInlineSuggestionUiCallback callback)46     public InlineSuggestionRoot(@NonNull Context context,
47             @NonNull IInlineSuggestionUiCallback callback) {
48         super(context);
49         mCallback = callback;
50         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
51         setFocusable(false);
52     }
53 
54     @Override
55     @SuppressLint("ClickableViewAccessibility")
dispatchTouchEvent(@onNull MotionEvent event)56     public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
57         switch (event.getActionMasked()) {
58             case MotionEvent.ACTION_DOWN: {
59                 mDownX = event.getX();
60                 mDownY = event.getY();
61             }
62             // Intentionally fall through to the next case so that when the window is obscured
63             // we transfer the touch to the remote IME window and don't handle it locally.
64 
65             case MotionEvent.ACTION_MOVE: {
66                 final float distance = MathUtils.dist(mDownX, mDownY,
67                         event.getX(), event.getY());
68                 final boolean isSecure = (event.getFlags()
69                         & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) == 0;
70                 if (!isSecure || distance > mTouchSlop) {
71                     try {
72                         mCallback.onTransferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
73                                 getContext().getDisplayId());
74                     } catch (RemoteException e) {
75                         Log.w(TAG, "RemoteException transferring touch focus to IME");
76                     }
77                 }
78             } break;
79         }
80         return super.dispatchTouchEvent(event);
81     }
82 }
83