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

Commit 9596f9c9 authored by Diego Vela's avatar Diego Vela
Browse files

Deduplicate reported fold features.

Deduplicate reported fold features.

Flag: EXEMPT bugfix
Bug: 336461584
Test: atest CtsInputMethodTestCases:InputMethodServiceTest
Change-Id: I3402c6a95ff975a7701d142e1405c62806932a22
parent c09dd42d
Loading
Loading
Loading
Loading
+22 −5
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.extensions.util.DeduplicateConsumer;

import java.util.ArrayList;
import java.util.Collections;
@@ -62,7 +63,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final Map<Context, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
    private final Map<Context, DeduplicateConsumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
            new ArrayMap<>();

    @GuardedBy("mLock")
@@ -130,7 +131,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
            if (mWindowLayoutChangeListeners.containsKey(context)
                    // In theory this method can be called on the same consumer with different
                    // context.
                    || mWindowLayoutChangeListeners.containsValue(consumer)) {
                    || containsConsumer(consumer)) {
                return;
            }
            if (!context.isUiContext()) {
@@ -141,7 +142,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
                WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features);
                consumer.accept(newWindowLayout);
            });
            mWindowLayoutChangeListeners.put(context, consumer);
            mWindowLayoutChangeListeners.put(context, new DeduplicateConsumer<>(consumer));

            final IBinder windowContextToken = context.getWindowContextToken();
            if (windowContextToken != null) {
@@ -176,20 +177,36 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    @Override
    public void removeWindowLayoutInfoListener(@NonNull Consumer<WindowLayoutInfo> consumer) {
        synchronized (mLock) {
            DeduplicateConsumer<WindowLayoutInfo> consumerToRemove = null;
            for (Context context : mWindowLayoutChangeListeners.keySet()) {
                if (!mWindowLayoutChangeListeners.get(context).equals(consumer)) {
                final DeduplicateConsumer<WindowLayoutInfo> deduplicateConsumer =
                        mWindowLayoutChangeListeners.get(context);
                if (!deduplicateConsumer.matchesConsumer(consumer)) {
                    continue;
                }
                final IBinder token = context.getWindowContextToken();
                consumerToRemove = deduplicateConsumer;
                if (token != null) {
                    context.unregisterComponentCallbacks(mConfigurationChangeListeners.get(token));
                    mConfigurationChangeListeners.remove(token);
                }
                break;
            }
            mWindowLayoutChangeListeners.values().remove(consumer);
            if (consumerToRemove != null) {
                mWindowLayoutChangeListeners.values().remove(consumerToRemove);
            }
        }
    }

    @GuardedBy("mLock")
    private boolean containsConsumer(@NonNull Consumer<WindowLayoutInfo> consumer) {
        for (DeduplicateConsumer<WindowLayoutInfo> c : mWindowLayoutChangeListeners.values()) {
            if (c.matchesConsumer(consumer)) {
                return true;
            }
        }
        return false;
    }

    @GuardedBy("mLock")
    private void updateListenerRegistrations() {
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 androidx.window.extensions.util;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.extensions.core.util.function.Consumer;

/**
 * A utility class that will not report a value if it is the same as the last reported value.
 * @param <T> generic values to be reported.
 */
public class DeduplicateConsumer<T> implements Consumer<T> {

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    @Nullable
    private T mLastReportedValue = null;
    @NonNull
    private final Consumer<T> mConsumer;

    public DeduplicateConsumer(@NonNull Consumer<T> consumer) {
        mConsumer = consumer;
    }

    /**
     * Returns {@code true} if the given consumer matches this object or the wrapped
     * {@link Consumer}, {@code false} otherwise
     */
    public boolean matchesConsumer(@NonNull Consumer<T> consumer) {
        return consumer == this || mConsumer.equals(consumer);
    }

    /**
     * Accepts a new value and relays it if it is different from
     * the last reported value.
     * @param value to report if different.
     */
    @Override
    public void accept(@NonNull T value) {
        synchronized (mLock) {
            if (mLastReportedValue != null && mLastReportedValue.equals(value)) {
                return;
            }
            mLastReportedValue = value;
        }
        mConsumer.accept(value);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ package {

android_test {
    name: "WMJetpackUnitTests",
    team: "trendy_team_windowing_sdk",
    // To make the test run via TEST_MAPPING
    test_suites: ["device-tests"],

@@ -32,6 +33,7 @@ android_test {

    static_libs: [
        "androidx.window.extensions",
        "androidx.window.extensions.core_core",
        "junit",
        "androidx.test.runner",
        "androidx.test.rules",
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 androidx.window.extensions.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import androidx.window.extensions.core.util.function.Consumer;

import org.junit.Test;

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

/**
 * A class to validate {@link DeduplicateConsumer}.
 */
public class DeduplicateConsumerTest {

    @Test
    public void test_duplicate_value_is_filtered() {
        String value = "test_value";
        List<String> expected = new ArrayList<>();
        expected.add(value);
        RecordingConsumer recordingConsumer = new RecordingConsumer();
        DeduplicateConsumer<String> deduplicateConsumer =
                new DeduplicateConsumer<>(recordingConsumer);

        deduplicateConsumer.accept(value);
        deduplicateConsumer.accept(value);

        assertEquals(expected, recordingConsumer.getValues());
    }

    @Test
    public void test_different_value_is_filtered() {
        String value = "test_value";
        String newValue = "test_value_new";
        List<String> expected = new ArrayList<>();
        expected.add(value);
        expected.add(newValue);
        RecordingConsumer recordingConsumer = new RecordingConsumer();
        DeduplicateConsumer<String> deduplicateConsumer =
                new DeduplicateConsumer<>(recordingConsumer);

        deduplicateConsumer.accept(value);
        deduplicateConsumer.accept(value);
        deduplicateConsumer.accept(newValue);

        assertEquals(expected, recordingConsumer.getValues());
    }

    @Test
    public void test_match_against_consumer_property_returns_true() {
        RecordingConsumer recordingConsumer = new RecordingConsumer();
        DeduplicateConsumer<String> deduplicateConsumer =
                new DeduplicateConsumer<>(recordingConsumer);

        assertTrue(deduplicateConsumer.matchesConsumer(recordingConsumer));
    }

    @Test
    public void test_match_against_self_returns_true() {
        RecordingConsumer recordingConsumer = new RecordingConsumer();
        DeduplicateConsumer<String> deduplicateConsumer =
                new DeduplicateConsumer<>(recordingConsumer);

        assertTrue(deduplicateConsumer.matchesConsumer(deduplicateConsumer));
    }

    private static final class RecordingConsumer implements Consumer<String> {

        private final List<String> mValues = new ArrayList<>();

        @Override
        public void accept(String s) {
            mValues.add(s);
        }

        public List<String> getValues() {
            return mValues;
        }
    }
}