Merge "TouchInsetManager Introduction." into tm-dev
This commit is contained in:
@@ -18,12 +18,9 @@ package com.android.systemui.dreams;
|
|||||||
|
|
||||||
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
|
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Region;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.systemui.R;
|
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).
|
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
|
||||||
private final Handler mHandler;
|
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
|
@Inject
|
||||||
public DreamOverlayContainerViewController(
|
public DreamOverlayContainerViewController(
|
||||||
DreamOverlayContainerView containerView,
|
DreamOverlayContainerView containerView,
|
||||||
@@ -136,16 +96,12 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onViewAttached() {
|
protected void onViewAttached() {
|
||||||
mView.getViewTreeObserver()
|
|
||||||
.addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
|
|
||||||
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
|
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onViewDetached() {
|
protected void onViewDetached() {
|
||||||
mHandler.removeCallbacks(this::updateBurnInOffsets);
|
mHandler.removeCallbacks(this::updateBurnInOffsets);
|
||||||
mView.getViewTreeObserver()
|
|
||||||
.removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
View getContainerView() {
|
View getContainerView() {
|
||||||
@@ -162,6 +118,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
|||||||
// so no translation occurs when the values don't change.
|
// so no translation occurs when the values don't change.
|
||||||
mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
|
mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
|
||||||
- mMaxBurnInOffset);
|
- mMaxBurnInOffset);
|
||||||
|
|
||||||
mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
|
mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
|
||||||
- mMaxBurnInOffset);
|
- mMaxBurnInOffset);
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.net.NetworkCapabilities;
|
|||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
|
|
||||||
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
|
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
|
||||||
|
import com.android.systemui.touch.TouchInsetManager;
|
||||||
import com.android.systemui.util.ViewController;
|
import com.android.systemui.util.ViewController;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -48,6 +49,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
|
|||||||
private static final int WIFI_STATUS_AVAILABLE = 2;
|
private static final int WIFI_STATUS_AVAILABLE = 2;
|
||||||
|
|
||||||
private final ConnectivityManager mConnectivityManager;
|
private final ConnectivityManager mConnectivityManager;
|
||||||
|
private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
|
||||||
|
|
||||||
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
|
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
|
||||||
.clearCapabilities()
|
.clearCapabilities()
|
||||||
@@ -77,9 +79,11 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
|
|||||||
@Inject
|
@Inject
|
||||||
public DreamOverlayStatusBarViewController(
|
public DreamOverlayStatusBarViewController(
|
||||||
DreamOverlayStatusBarView view,
|
DreamOverlayStatusBarView view,
|
||||||
ConnectivityManager connectivityManager) {
|
ConnectivityManager connectivityManager,
|
||||||
|
TouchInsetManager.TouchInsetSession touchInsetSession) {
|
||||||
super(view);
|
super(view);
|
||||||
mConnectivityManager = connectivityManager;
|
mConnectivityManager = connectivityManager;
|
||||||
|
mTouchInsetSession = touchInsetSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,11 +96,13 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
|
|||||||
onWifiAvailabilityChanged(
|
onWifiAvailabilityChanged(
|
||||||
capabilities != null
|
capabilities != null
|
||||||
&& capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
|
&& capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
|
||||||
|
mTouchInsetSession.addViewToTracking(mView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onViewDetached() {
|
protected void onViewDetached() {
|
||||||
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
|
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
|
||||||
|
mTouchInsetSession.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
|
|||||||
import androidx.constraintlayout.widget.Constraints;
|
import androidx.constraintlayout.widget.Constraints;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.touch.TouchInsetManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -52,6 +53,7 @@ public class ComplicationLayoutEngine {
|
|||||||
private static class ViewEntry implements Comparable<ViewEntry> {
|
private static class ViewEntry implements Comparable<ViewEntry> {
|
||||||
private final View mView;
|
private final View mView;
|
||||||
private final ComplicationLayoutParams mLayoutParams;
|
private final ComplicationLayoutParams mLayoutParams;
|
||||||
|
private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
|
||||||
private final Parent mParent;
|
private final Parent mParent;
|
||||||
@Complication.Category
|
@Complication.Category
|
||||||
private final int mCategory;
|
private final int mCategory;
|
||||||
@@ -61,7 +63,8 @@ public class ComplicationLayoutEngine {
|
|||||||
* Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
|
* Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
|
||||||
* view hierarchy to be accessed without traversing the entire view tree.
|
* 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) {
|
int margin) {
|
||||||
mView = view;
|
mView = view;
|
||||||
// Views that are generated programmatically do not have a unique id assigned to them
|
// 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.
|
// {@link Complication.ViewHolder} should not reference the root container by id.
|
||||||
mView.setId(View.generateViewId());
|
mView.setId(View.generateViewId());
|
||||||
mLayoutParams = layoutParams;
|
mLayoutParams = layoutParams;
|
||||||
|
mTouchInsetSession = touchSession;
|
||||||
mCategory = category;
|
mCategory = category;
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
mMargin = margin;
|
mMargin = margin;
|
||||||
|
|
||||||
|
touchSession.addViewToTracking(mView);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,6 +223,7 @@ public class ComplicationLayoutEngine {
|
|||||||
mParent.removeEntry(this);
|
mParent.removeEntry(this);
|
||||||
|
|
||||||
((ViewGroup) mView.getParent()).removeView(mView);
|
((ViewGroup) mView.getParent()).removeView(mView);
|
||||||
|
mTouchInsetSession.removeViewFromTracking(mView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -242,15 +249,18 @@ public class ComplicationLayoutEngine {
|
|||||||
*/
|
*/
|
||||||
private static class Builder {
|
private static class Builder {
|
||||||
private final View mView;
|
private final View mView;
|
||||||
|
private final TouchInsetManager.TouchInsetSession mTouchSession;
|
||||||
private final ComplicationLayoutParams mLayoutParams;
|
private final ComplicationLayoutParams mLayoutParams;
|
||||||
private final int mCategory;
|
private final int mCategory;
|
||||||
private Parent mParent;
|
private Parent mParent;
|
||||||
private int mMargin;
|
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;
|
mView = view;
|
||||||
mLayoutParams = lp;
|
mLayoutParams = lp;
|
||||||
mCategory = category;
|
mCategory = category;
|
||||||
|
mTouchSession = touchSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,7 +301,8 @@ public class ComplicationLayoutEngine {
|
|||||||
* Builds and returns the resulting {@link ViewEntry}.
|
* Builds and returns the resulting {@link ViewEntry}.
|
||||||
*/
|
*/
|
||||||
ViewEntry build() {
|
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 int mMargin;
|
||||||
private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
|
private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
|
||||||
private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
|
private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
|
||||||
|
private final TouchInsetManager.TouchInsetSession mSession;
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
@Inject
|
@Inject
|
||||||
public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
|
public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
|
||||||
@Named(COMPLICATION_MARGIN) int margin) {
|
@Named(COMPLICATION_MARGIN) int margin,
|
||||||
|
TouchInsetManager.TouchInsetSession session) {
|
||||||
mLayout = layout;
|
mLayout = layout;
|
||||||
mMargin = margin;
|
mMargin = margin;
|
||||||
|
mSession = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -468,7 +482,7 @@ public class ComplicationLayoutEngine {
|
|||||||
removeComplication(id);
|
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);
|
.setMargin(mMargin);
|
||||||
|
|
||||||
// Add position group if doesn't already exist
|
// Add position group if doesn't already exist
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.dreams.DreamOverlayContainerView;
|
import com.android.systemui.dreams.DreamOverlayContainerView;
|
||||||
import com.android.systemui.dreams.DreamOverlayStatusBarView;
|
import com.android.systemui.dreams.DreamOverlayStatusBarView;
|
||||||
|
import com.android.systemui.touch.TouchInsetManager;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
@@ -63,6 +66,21 @@ public abstract class DreamOverlayModule {
|
|||||||
"R.id.dream_overlay_content must not be null");
|
"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
|
@Provides
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
@DreamOverlayComponent.DreamOverlayScope
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
package com.android.systemui.dreams;
|
package com.android.systemui.dreams;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -117,31 +116,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
|||||||
DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT);
|
DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnViewAttachedRegistersComputeInsetsListener() {
|
|
||||||
mController.onViewAttached();
|
|
||||||
verify(mViewTreeObserver).addOnComputeInternalInsetsListener(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnViewDetachedUnregistersComputeInsetsListener() {
|
|
||||||
mController.onViewDetached();
|
|
||||||
verify(mViewTreeObserver).removeOnComputeInternalInsetsListener(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testComputeInsetsListenerReturnsRegion() {
|
|
||||||
final ArgumentCaptor<ViewTreeObserver.OnComputeInternalInsetsListener>
|
|
||||||
computeInsetsListenerCapture =
|
|
||||||
ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener.class);
|
|
||||||
mController.onViewAttached();
|
|
||||||
verify(mViewTreeObserver).addOnComputeInternalInsetsListener(
|
|
||||||
computeInsetsListenerCapture.capture());
|
|
||||||
final ViewTreeObserver.InternalInsetsInfo info = new ViewTreeObserver.InternalInsetsInfo();
|
|
||||||
computeInsetsListenerCapture.getValue().onComputeInternalInsets(info);
|
|
||||||
assertNotNull(info.touchableRegion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBurnInProtectionStartsWhenContentViewAttached() {
|
public void testBurnInProtectionStartsWhenContentViewAttached() {
|
||||||
mController.onViewAttached();
|
mController.onViewAttached();
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.testing.AndroidTestingRunner;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.touch.TouchInsetManager;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -49,13 +50,16 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
|
|||||||
NetworkCapabilities mNetworkCapabilities;
|
NetworkCapabilities mNetworkCapabilities;
|
||||||
@Mock
|
@Mock
|
||||||
Network mNetwork;
|
Network mNetwork;
|
||||||
|
@Mock
|
||||||
|
TouchInsetManager.TouchInsetSession mTouchSession;
|
||||||
|
|
||||||
DreamOverlayStatusBarViewController mController;
|
DreamOverlayStatusBarViewController mController;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager);
|
mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager,
|
||||||
|
mTouchSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import androidx.test.filters.SmallTest;
|
|||||||
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.touch.TouchInsetManager;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -46,6 +47,9 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
@Mock
|
@Mock
|
||||||
ConstraintLayout mLayout;
|
ConstraintLayout mLayout;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
TouchInsetManager.TouchInsetSession mTouchSession;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
@@ -112,7 +116,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
Complication.CATEGORY_STANDARD,
|
Complication.CATEGORY_STANDARD,
|
||||||
mLayout);
|
mLayout);
|
||||||
|
|
||||||
final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
|
final ComplicationLayoutEngine engine =
|
||||||
|
new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
|
||||||
addComplication(engine, firstViewInfo);
|
addComplication(engine, firstViewInfo);
|
||||||
|
|
||||||
// Ensure the view is added to the top end corner
|
// Ensure the view is added to the top end corner
|
||||||
@@ -139,7 +144,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
Complication.CATEGORY_STANDARD,
|
Complication.CATEGORY_STANDARD,
|
||||||
mLayout);
|
mLayout);
|
||||||
|
|
||||||
final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
|
final ComplicationLayoutEngine engine =
|
||||||
|
new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
|
||||||
addComplication(engine, firstViewInfo);
|
addComplication(engine, firstViewInfo);
|
||||||
|
|
||||||
// Ensure the view is added to the top end corner
|
// Ensure the view is added to the top end corner
|
||||||
@@ -155,7 +161,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDirectionLayout() {
|
public void testDirectionLayout() {
|
||||||
final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
|
final ComplicationLayoutEngine engine =
|
||||||
|
new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
|
||||||
|
|
||||||
final ViewInfo firstViewInfo = new ViewInfo(
|
final ViewInfo firstViewInfo = new ViewInfo(
|
||||||
new ComplicationLayoutParams(
|
new ComplicationLayoutParams(
|
||||||
@@ -203,7 +210,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testPositionLayout() {
|
public void testPositionLayout() {
|
||||||
final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
|
final ComplicationLayoutEngine engine =
|
||||||
|
new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
|
||||||
|
|
||||||
final ViewInfo firstViewInfo = new ViewInfo(
|
final ViewInfo firstViewInfo = new ViewInfo(
|
||||||
new ComplicationLayoutParams(
|
new ComplicationLayoutParams(
|
||||||
@@ -290,7 +298,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testMargin() {
|
public void testMargin() {
|
||||||
final int margin = 5;
|
final int margin = 5;
|
||||||
final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, margin);
|
final ComplicationLayoutEngine engine =
|
||||||
|
new ComplicationLayoutEngine(mLayout, margin, mTouchSession);
|
||||||
|
|
||||||
final ViewInfo firstViewInfo = new ViewInfo(
|
final ViewInfo firstViewInfo = new ViewInfo(
|
||||||
new ComplicationLayoutParams(
|
new ComplicationLayoutParams(
|
||||||
@@ -364,7 +373,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testRemoval() {
|
public void testRemoval() {
|
||||||
final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
|
final ComplicationLayoutEngine engine =
|
||||||
|
new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
|
||||||
|
|
||||||
final ViewInfo firstViewInfo = new ViewInfo(
|
final ViewInfo firstViewInfo = new ViewInfo(
|
||||||
new ComplicationLayoutParams(
|
new ComplicationLayoutParams(
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* 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 static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Region;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewRootImpl;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
public class TouchInsetManagerTest extends SysuiTestCase {
|
||||||
|
@Mock
|
||||||
|
private View mRootView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ViewRootImpl mRootViewImpl;
|
||||||
|
|
||||||
|
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
when(mRootView.getViewRootImpl()).thenReturn(mRootViewImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRootViewOnAttachedHandling() {
|
||||||
|
// Create inset manager
|
||||||
|
final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
|
||||||
|
mRootView);
|
||||||
|
|
||||||
|
final ArgumentCaptor<View.OnAttachStateChangeListener> listener =
|
||||||
|
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
|
||||||
|
|
||||||
|
// Ensure manager has registered to listen to attached state of root view.
|
||||||
|
verify(mRootView).addOnAttachStateChangeListener(listener.capture());
|
||||||
|
|
||||||
|
// Trigger attachment and verify touchable region is set.
|
||||||
|
listener.getValue().onViewAttachedToWindow(mRootView);
|
||||||
|
verify(mRootViewImpl).setTouchableRegion(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInsetRegionPropagation() {
|
||||||
|
// Create inset manager
|
||||||
|
final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
|
||||||
|
mRootView);
|
||||||
|
|
||||||
|
// Create session
|
||||||
|
final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
|
||||||
|
|
||||||
|
// Add a view to the session.
|
||||||
|
final Rect rect = new Rect(0, 0, 2, 2);
|
||||||
|
|
||||||
|
session.addViewToTracking(createView(rect));
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
|
// Check to see if view was properly accounted for.
|
||||||
|
final Region expectedRegion = Region.obtain();
|
||||||
|
expectedRegion.op(rect, Region.Op.UNION);
|
||||||
|
verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleRegions() {
|
||||||
|
// Create inset manager
|
||||||
|
final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
|
||||||
|
mRootView);
|
||||||
|
|
||||||
|
// Create session
|
||||||
|
final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
|
||||||
|
|
||||||
|
// Add a view to the session.
|
||||||
|
final Rect firstBounds = new Rect(0, 0, 2, 2);
|
||||||
|
session.addViewToTracking(createView(firstBounds));
|
||||||
|
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
clearInvocations(mRootViewImpl);
|
||||||
|
|
||||||
|
// Create second session
|
||||||
|
final TouchInsetManager.TouchInsetSession secondSession = insetManager.createSession();
|
||||||
|
|
||||||
|
// Add a view to the second session.
|
||||||
|
final Rect secondBounds = new Rect(4, 4, 8, 10);
|
||||||
|
secondSession.addViewToTracking(createView(secondBounds));
|
||||||
|
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
|
// Check to see if all views and sessions was properly accounted for.
|
||||||
|
{
|
||||||
|
final Region expectedRegion = Region.obtain();
|
||||||
|
expectedRegion.op(firstBounds, Region.Op.UNION);
|
||||||
|
expectedRegion.op(secondBounds, Region.Op.UNION);
|
||||||
|
verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clearInvocations(mRootViewImpl);
|
||||||
|
|
||||||
|
// clear first session, ensure second session is still reflected.
|
||||||
|
session.clear();
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
{
|
||||||
|
final Region expectedRegion = Region.obtain();
|
||||||
|
expectedRegion.op(firstBounds, Region.Op.UNION);
|
||||||
|
verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleViews() {
|
||||||
|
// Create inset manager
|
||||||
|
final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
|
||||||
|
mRootView);
|
||||||
|
|
||||||
|
// Create session
|
||||||
|
final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
|
||||||
|
|
||||||
|
// Add a view to the session.
|
||||||
|
final Rect firstViewBounds = new Rect(0, 0, 2, 2);
|
||||||
|
session.addViewToTracking(createView(firstViewBounds));
|
||||||
|
|
||||||
|
// only capture second invocation.
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
clearInvocations(mRootViewImpl);
|
||||||
|
|
||||||
|
// Add a second view to the session
|
||||||
|
final Rect secondViewBounds = new Rect(4, 4, 9, 10);
|
||||||
|
final View secondView = createView(secondViewBounds);
|
||||||
|
session.addViewToTracking(secondView);
|
||||||
|
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
|
// Check to see if all views and sessions was properly accounted for.
|
||||||
|
{
|
||||||
|
final Region expectedRegion = Region.obtain();
|
||||||
|
expectedRegion.op(firstViewBounds, Region.Op.UNION);
|
||||||
|
expectedRegion.op(secondViewBounds, Region.Op.UNION);
|
||||||
|
verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove second view.
|
||||||
|
session.removeViewFromTracking(secondView);
|
||||||
|
|
||||||
|
clearInvocations(mRootViewImpl);
|
||||||
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
|
// Ensure first view still reflected in touch region.
|
||||||
|
{
|
||||||
|
final Region expectedRegion = Region.obtain();
|
||||||
|
expectedRegion.op(firstViewBounds, Region.Op.UNION);
|
||||||
|
verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private View createView(Rect bounds) {
|
||||||
|
final Rect rect = new Rect(bounds);
|
||||||
|
final View view = Mockito.mock(View.class);
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
((Rect) invocation.getArgument(0)).set(rect);
|
||||||
|
return null;
|
||||||
|
}).when(view).getBoundsOnScreen(any());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user