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

Commit 3fe6df25 authored by Diego Vela's avatar Diego Vela Committed by Android (Google) Code Review
Browse files

Merge "Deduplicate reported fold features." into main

parents afb5f5e9 9596f9c9
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;
        }
    }
}