chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/CompositeTouchDelegate.java

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.browser_ui.widget;

import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Allows multiple touch delegates for one parent view.
 * When to use this class:
 *   You need add touch delegates to two or more views that share the same parent.
 * To use this class:
 * 1. Add this as an instance variable to an ancestor view.
 * 2. Set the CompositeTouchDelegate to the ancestor view's TouchDelegate slot (setTouchDelegate).
 * 2. Pass the CompositeTouchDelegate instance into the descendant views.
 * 3. Register the descendant's TouchDelegate with the supplied CompositeTouchDelegate using
 *    {@link #addDelegateForDescendantView}.
 *
 * Note that it is expected that the registered touch delegates will each handle a different region
 * of the ancestor view. Only the first matching TouchDelegate that can handle a touch event will be
 * sent the event.
 */
public class CompositeTouchDelegate extends TouchDelegate {
    private final List<TouchDelegate> mDelegates = new ArrayList<>();

    /** @param view Used to get the context. */
    public CompositeTouchDelegate(View view) {
        super(new Rect(), view);
    }

    /**
     * Add a delegate to the composite.
     * @param delegate Delegate to be added, this will receive touch events.
     */
    public void addDelegateForDescendantView(TouchDelegate delegate) {
        mDelegates.add(delegate);
    }

    /**
     * Remove the delegate from the composite.
     * @param delegate Delegate to be removed.
     */
    public void removeDelegateForDescendantView(TouchDelegate delegate) {
        mDelegates.remove(delegate);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        for (TouchDelegate delegate : mDelegates) {
            event.setLocation(x, y);
            boolean wasTouchEventHandled = delegate.onTouchEvent(event);
            if (wasTouchEventHandled) return true;
        }

        return false;
    }
}