Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eaa45195 authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge "TouchInsetManager Introduction." into tm-dev

parents eefc676f 9bdbf292
Loading
Loading
Loading
Loading
+1 −44
Original line number Diff line number Diff line
@@ -18,12 +18,9 @@ package com.android.systemui.dreams;

import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;

import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
@@ -62,43 +59,6 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
    // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
    private final Handler mHandler;

    // A hook into the internal inset calculation where we declare the overlays as the only
    // touchable regions.
    private final ViewTreeObserver.OnComputeInternalInsetsListener
            mOnComputeInternalInsetsListener =
            new ViewTreeObserver.OnComputeInternalInsetsListener() {
                @Override
                public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
                    inoutInfo.setTouchableInsets(
                            ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
                    final Region region = new Region();
                    final Rect rect = new Rect();
                    final int childCount = mDreamOverlayContentView.getChildCount();
                    for (int i = 0; i < childCount; i++) {
                        View child = mDreamOverlayContentView.getChildAt(i);

                        if (mComplicationHostViewController.getView() == child) {
                            region.op(mComplicationHostViewController.getTouchRegions(),
                                    Region.Op.UNION);
                            continue;
                        }

                        if (child.getGlobalVisibleRect(rect)) {
                            region.op(rect, Region.Op.UNION);
                        }
                    }

                    // Add the notifications drag area to the tap region (otherwise the
                    // notifications shade can't be dragged down).
                    if (mDreamOverlayContentView.getGlobalVisibleRect(rect)) {
                        rect.bottom = rect.top + mDreamOverlayNotificationsDragAreaHeight;
                        region.op(rect, Region.Op.UNION);
                    }

                    inoutInfo.touchableRegion.set(region);
                }
            };

    @Inject
    public DreamOverlayContainerViewController(
            DreamOverlayContainerView containerView,
@@ -136,16 +96,12 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve

    @Override
    protected void onViewAttached() {
        mView.getViewTreeObserver()
                .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
    }

    @Override
    protected void onViewDetached() {
        mHandler.removeCallbacks(this::updateBurnInOffsets);
        mView.getViewTreeObserver()
                .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
    }

    View getContainerView() {
@@ -162,6 +118,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
        // so no translation occurs when the values don't change.
        mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
                - mMaxBurnInOffset);

        mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
                - mMaxBurnInOffset);

+7 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkRequest;

import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;

import java.lang.annotation.Retention;
@@ -48,6 +49,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
    private static final int WIFI_STATUS_AVAILABLE = 2;

    private final ConnectivityManager mConnectivityManager;
    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;

    private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
            .clearCapabilities()
@@ -77,9 +79,11 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
    @Inject
    public DreamOverlayStatusBarViewController(
            DreamOverlayStatusBarView view,
            ConnectivityManager connectivityManager) {
            ConnectivityManager connectivityManager,
            TouchInsetManager.TouchInsetSession touchInsetSession) {
        super(view);
        mConnectivityManager = connectivityManager;
        mTouchInsetSession = touchInsetSession;
    }

    @Override
@@ -92,11 +96,13 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
        onWifiAvailabilityChanged(
                capabilities != null
                        && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
        mTouchInsetSession.addViewToTracking(mView);
    }

    @Override
    protected void onViewDetached() {
        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
        mTouchInsetSession.clear();
    }

    /**
+19 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Constraints;

import com.android.systemui.R;
import com.android.systemui.touch.TouchInsetManager;

import java.util.ArrayList;
import java.util.Collections;
@@ -52,6 +53,7 @@ public class ComplicationLayoutEngine {
    private static class ViewEntry implements Comparable<ViewEntry> {
        private final View mView;
        private final ComplicationLayoutParams mLayoutParams;
        private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
        private final Parent mParent;
        @Complication.Category
        private final int mCategory;
@@ -61,7 +63,8 @@ public class ComplicationLayoutEngine {
         * Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
         * view hierarchy to be accessed without traversing the entire view tree.
         */
        ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent,
        ViewEntry(View view, ComplicationLayoutParams layoutParams,
                TouchInsetManager.TouchInsetSession touchSession, int category, Parent parent,
                int margin) {
            mView = view;
            // Views that are generated programmatically do not have a unique id assigned to them
@@ -70,9 +73,12 @@ public class ComplicationLayoutEngine {
            // {@link Complication.ViewHolder} should not reference the root container by id.
            mView.setId(View.generateViewId());
            mLayoutParams = layoutParams;
            mTouchInsetSession = touchSession;
            mCategory = category;
            mParent = parent;
            mMargin = margin;

            touchSession.addViewToTracking(mView);
        }

        /**
@@ -217,6 +223,7 @@ public class ComplicationLayoutEngine {
            mParent.removeEntry(this);

            ((ViewGroup) mView.getParent()).removeView(mView);
            mTouchInsetSession.removeViewFromTracking(mView);
        }

        @Override
@@ -242,15 +249,18 @@ public class ComplicationLayoutEngine {
         */
        private static class Builder {
            private final View mView;
            private final TouchInsetManager.TouchInsetSession mTouchSession;
            private final ComplicationLayoutParams mLayoutParams;
            private final int mCategory;
            private Parent mParent;
            private int mMargin;

            Builder(View view, ComplicationLayoutParams lp, @Complication.Category int category) {
            Builder(View view, TouchInsetManager.TouchInsetSession touchSession,
                    ComplicationLayoutParams lp, @Complication.Category int category) {
                mView = view;
                mLayoutParams = lp;
                mCategory = category;
                mTouchSession = touchSession;
            }

            /**
@@ -291,7 +301,8 @@ public class ComplicationLayoutEngine {
             * Builds and returns the resulting {@link ViewEntry}.
             */
            ViewEntry build() {
                return new ViewEntry(mView, mLayoutParams, mCategory, mParent, mMargin);
                return new ViewEntry(mView, mLayoutParams, mTouchSession, mCategory, mParent,
                        mMargin);
            }
        }

@@ -442,13 +453,16 @@ public class ComplicationLayoutEngine {
    private final int mMargin;
    private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
    private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
    private final TouchInsetManager.TouchInsetSession mSession;

    /** */
    @Inject
    public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
            @Named(COMPLICATION_MARGIN) int margin) {
            @Named(COMPLICATION_MARGIN) int margin,
            TouchInsetManager.TouchInsetSession session) {
        mLayout = layout;
        mMargin = margin;
        mSession = session;
    }

    /**
@@ -468,7 +482,7 @@ public class ComplicationLayoutEngine {
            removeComplication(id);
        }

        final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category)
        final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, mSession, lp, category)
                .setMargin(mMargin);

        // Add position group if doesn't already exist
+18 −0
Original line number Diff line number Diff line
@@ -29,6 +29,9 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.dreams.DreamOverlayStatusBarView;
import com.android.systemui.touch.TouchInsetManager;

import java.util.concurrent.Executor;

import javax.inject.Named;

@@ -63,6 +66,21 @@ public abstract class DreamOverlayModule {
                "R.id.dream_overlay_content must not be null");
    }

    /** */
    @Provides
    public static TouchInsetManager.TouchInsetSession providesTouchInsetSession(
            TouchInsetManager manager) {
        return manager.createSession();
    }

    /** */
    @Provides
    @DreamOverlayComponent.DreamOverlayScope
    public static TouchInsetManager providesTouchInsetManager(@Main Executor executor,
            DreamOverlayContainerView view) {
        return new TouchInsetManager(executor, view);
    }

    /** */
    @Provides
    @DreamOverlayComponent.DreamOverlayScope
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.touch;

import android.graphics.Rect;
import android.graphics.Region;
import android.view.View;
import android.view.ViewRootImpl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.Executor;

/**
 * {@link TouchInsetManager} handles setting the touchable inset regions for a given View. This
 * is useful for passing through touch events for all but select areas.
 */
public class TouchInsetManager {
    /**
     * {@link TouchInsetSession} provides an individualized session with the
     * {@link TouchInsetManager}, linking any action to the client.
     */
    public static class TouchInsetSession {
        private final TouchInsetManager mManager;

        private final HashSet<View> mTrackedViews;
        private final Executor mExecutor;

        private final View.OnLayoutChangeListener mOnLayoutChangeListener =
                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
                        -> updateTouchRegion();

        /**
         * Default constructor
         * @param manager The parent {@link TouchInsetManager} which will be affected by actions on
         *                this session.
         * @param rootView The parent of views that will be tracked.
         * @param executor An executor for marshalling operations.
         */
        TouchInsetSession(TouchInsetManager manager, Executor executor) {
            mManager = manager;
            mTrackedViews = new HashSet<>();
            mExecutor = executor;
        }

        /**
         * Adds a descendant of the root view to be tracked.
         * @param view {@link View} to be tracked.
         */
        public void addViewToTracking(View view) {
            mExecutor.execute(() -> {
                mTrackedViews.add(view);
                view.addOnLayoutChangeListener(mOnLayoutChangeListener);
                updateTouchRegion();
            });
        }

        /**
         * Removes a view from further tracking
         * @param view {@link View} to be removed.
         */
        public void removeViewFromTracking(View view) {
            mExecutor.execute(() -> {
                mTrackedViews.remove(view);
                view.removeOnLayoutChangeListener(mOnLayoutChangeListener);
                updateTouchRegion();
            });
        }

        private void updateTouchRegion() {
            final Region cumulativeRegion = Region.obtain();

            mTrackedViews.stream().forEach(view -> {
                final Rect boundaries = new Rect();
                view.getBoundsOnScreen(boundaries);
                cumulativeRegion.op(boundaries, Region.Op.UNION);
            });

            mManager.setTouchRegion(this, cumulativeRegion);

            cumulativeRegion.recycle();
        }

        /**
         * Removes all tracked views and updates insets accordingly.
         */
        public void clear() {
            mExecutor.execute(() -> {
                mManager.clearRegion(this);
                mTrackedViews.clear();
            });
        }
    }

    private final HashMap<TouchInsetSession, Region> mDefinedRegions = new HashMap<>();
    private final Executor mExecutor;
    private final View mRootView;

    private final View.OnAttachStateChangeListener mAttachListener =
            new View.OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(View v) {
                    updateTouchInset();
                }

                @Override
                public void onViewDetachedFromWindow(View v) {
                }
            };

    /**
     * Default constructor.
     * @param executor An {@link Executor} to marshal all operations on.
     * @param rootView The root {@link View} for all views in sessions.
     */
    public TouchInsetManager(Executor executor, View rootView) {
        mExecutor = executor;
        mRootView = rootView;
        mRootView.addOnAttachStateChangeListener(mAttachListener);

    }

    /**
     * Creates a new associated session.
     */
    public TouchInsetSession createSession() {
        return new TouchInsetSession(this, mExecutor);
    }

    private void updateTouchInset() {
        final ViewRootImpl viewRootImpl = mRootView.getViewRootImpl();

        if (viewRootImpl == null) {
            return;
        }

        final Region aggregateRegion = Region.obtain();

        for (Region region : mDefinedRegions.values()) {
            aggregateRegion.op(region, Region.Op.UNION);
        }

        viewRootImpl.setTouchableRegion(aggregateRegion);

        aggregateRegion.recycle();
    }

    protected void setTouchRegion(TouchInsetSession session, Region region) {
        final Region introducedRegion = Region.obtain(region);
        mExecutor.execute(() -> {
            mDefinedRegions.put(session, introducedRegion);
            updateTouchInset();
        });
    }

    private void clearRegion(TouchInsetSession session) {
        mExecutor.execute(() -> {
            final Region storedRegion = mDefinedRegions.remove(session);

            if (storedRegion != null) {
                storedRegion.recycle();
            }

            updateTouchInset();
        });
    }
}
Loading