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

Commit 0906c162 authored by Coco Duan's avatar Coco Duan
Browse files

Turn off glanceable hub touch handling on dream if disabled

- Added an `isEnabled` field in DreamTouchHandler interface which
  defaults to true.
- If communal is not available, set enabled state to false in
  CommunalTouchHander, so that no touch session or region
  will be initiated trying to handle touch for the glanceable hub.

Bug: b/323366236
Test: edge swipe on dream with GH enabled/disabled
Test: atest CommunalTouchHandlerTest
Flag: ACONFIG com.android.systemui.communal_hub STAGING
Change-Id: Ifa5941c27aa7dc55e25f0e3277b1e5cedde38838
parent 3f220263
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -18,15 +18,20 @@ package com.android.systemui.dreams.touch;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.view.GestureDetector;
import android.view.MotionEvent;

import androidx.lifecycle.Lifecycle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.CentralSurfaces;

@@ -39,28 +44,60 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class CommunalTouchHandlerTest extends SysuiTestCase {
    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);

    @Mock
    CentralSurfaces mCentralSurfaces;
    @Mock
    DreamTouchHandler.TouchSession mTouchSession;
    CommunalTouchHandler mTouchHandler;
    @Mock
    Lifecycle mLifecycle;

    private static final int INITIATION_WIDTH = 20;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        AtomicReference reference = new AtomicReference<>(null);
        when(mLifecycle.getInternalScopeRef()).thenReturn(reference);
        when(mLifecycle.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
        mTouchHandler = new CommunalTouchHandler(
                Optional.of(mCentralSurfaces),
                INITIATION_WIDTH);
                INITIATION_WIDTH,
                mKosmos.getCommunalInteractor(),
                mLifecycle
                );
    }

    @Test
    public void communalTouchHandler_disabledByDefault() {
        assertThat(mTouchHandler.isEnabled()).isFalse();
    }

    @Test
    public void communalTouchHandler_disabled_whenCommunalUnavailable() {
        mTouchHandler.mIsCommunalAvailableCallback.accept(false);
        assertThat(mTouchHandler.isEnabled()).isFalse();

        mTouchHandler.onSessionStart(mTouchSession);
        verify(mTouchSession, never()).registerGestureListener(any());
    }

    @Test
    public void communalTouchHandler_enabled_whenCommunalAvailable() {
        mTouchHandler.mIsCommunalAvailableCallback.accept(true);
        assertThat(mTouchHandler.isEnabled()).isTrue();
    }

    @Test
    public void testEventPropagation() {
        mTouchHandler.mIsCommunalAvailableCallback.accept(true);
        final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);

        final ArgumentCaptor<InputChannelCompat.InputEventListener>
@@ -75,6 +112,7 @@ public class CommunalTouchHandlerTest extends SysuiTestCase {

    @Test
    public void testTouchPilferingOnScroll() {
        mTouchHandler.mIsCommunalAvailableCallback.accept(true);
        final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
        final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);

+39 −1
Original line number Diff line number Diff line
@@ -17,15 +17,21 @@
package com.android.systemui.dreams.touch;

import static com.android.systemui.dreams.touch.dagger.ShadeModule.COMMUNAL_GESTURE_INITIATION_WIDTH;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;

import android.graphics.Rect;
import android.graphics.Region;
import android.view.GestureDetector;
import android.view.MotionEvent;

import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;

import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.statusbar.phone.CentralSurfaces;

import java.util.Optional;
import java.util.function.Consumer;

import javax.inject.Inject;
import javax.inject.Named;
@@ -34,17 +40,49 @@ import javax.inject.Named;
public class CommunalTouchHandler implements DreamTouchHandler {
    private final int mInitiationWidth;
    private final Optional<CentralSurfaces> mCentralSurfaces;
    private final Lifecycle mLifecycle;
    private final CommunalInteractor mCommunalInteractor;
    private Boolean mIsEnabled = false;

    @VisibleForTesting
    final Consumer<Boolean> mIsCommunalAvailableCallback =
            isAvailable -> {
                setIsEnabled(isAvailable);
            };

    @Inject
    public CommunalTouchHandler(
            Optional<CentralSurfaces> centralSurfaces,
            @Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth) {
            @Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth,
            CommunalInteractor communalInteractor,
            Lifecycle lifecycle) {
        mInitiationWidth = initiationWidth;
        mCentralSurfaces = centralSurfaces;
        mLifecycle = lifecycle;
        mCommunalInteractor = communalInteractor;

        collectFlow(
                mLifecycle,
                mCommunalInteractor.isCommunalAvailable(),
                mIsCommunalAvailableCallback
        );
    }

    @Override
    public Boolean isEnabled() {
        return mIsEnabled;
    }

    @Override
    public void setIsEnabled(Boolean enabled) {
        mIsEnabled = enabled;
    }

    @Override
    public void onSessionStart(TouchSession session) {
        if (!mIsEnabled) {
            return;
        }
        mCentralSurfaces.ifPresent(surfaces -> handleSessionStart(surfaces, session));
    }

+3 −0
Original line number Diff line number Diff line
@@ -292,6 +292,9 @@ public class DreamOverlayTouchMonitor {
                        new HashMap<>();

                for (DreamTouchHandler handler : mHandlers) {
                            if (!handler.isEnabled()) {
                                continue;
                            }
                    final Rect maxBounds = mDisplayHelper.getMaxBounds(ev.getDisplayId(),
                            TYPE_APPLICATION_OVERLAY);

+14 −0
Original line number Diff line number Diff line
@@ -85,6 +85,20 @@ public interface DreamTouchHandler {
        Rect getBounds();
    }

    /**
     * Returns whether the handler is enabled to handle touch on dream.
     * @return isEnabled state. By default it's true.
     */
    default Boolean isEnabled() {
        return true;
    }

    /**
     * Sets whether to enable the handler to handle touch on dream.
     * @param enabled new value to be set whether to enable the handler.
     */
    default void setIsEnabled(Boolean enabled){}

    /**
     * Returns the region the touch handler is interested in. By default, no region is specified,
     * indicating the entire screen should be considered.
+18 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.util.kotlin

import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -76,6 +77,23 @@ fun <T> collectFlow(
    }
}

/**
 * Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to
 * [LifeCycle.State.CREATED] which is mapped over from the equivalent definition for collecting the
 * flow on a view.
 */
@JvmOverloads
fun <T> collectFlow(
    lifecycle: Lifecycle,
    flow: Flow<T>,
    consumer: Consumer<T>,
    state: Lifecycle.State = Lifecycle.State.CREATED,
) {
    lifecycle.coroutineScope.launch {
        lifecycle.repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
    }
}

fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
    return combine(flow1, flow2, bifunction)
}
Loading