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

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

Merge "Add raw configuration change listener updates." into udc-qpr-dev-plus-aosp

parents 43cf0e88 852dd9d8
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

/**
 * This manages the execution of the main thread in an
@@ -369,6 +370,11 @@ public final class ActivityThread extends ClientTransactionHandler
    @GuardedBy("mAppThread")
    private int mLastProcessState = PROCESS_STATE_UNKNOWN;
    final ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>();

    @NonNull
    private final ConfigurationChangedListenerController mConfigurationChangedListenerController =
            new ConfigurationChangedListenerController();

    private int mLastSessionId;
    // Holds the value of the last reported device ID value from the server for the top activity.
    int mLastReportedDeviceId;
@@ -3536,6 +3542,21 @@ public final class ActivityThread extends ClientTransactionHandler
        return mConfigurationController.getConfiguration();
    }

    /**
     * @hide
     */
    public void addConfigurationChangedListener(Executor executor,
            Consumer<IBinder> consumer) {
        mConfigurationChangedListenerController.addListener(executor, consumer);
    }

    /**
     * @hide
     */
    public void removeConfigurationChangedListener(Consumer<IBinder> consumer) {
        mConfigurationChangedListenerController.removeListener(consumer);
    }

    @Override
    public void updatePendingConfiguration(Configuration config) {
        final Configuration updatedConfig =
@@ -6094,6 +6115,8 @@ public final class ActivityThread extends ClientTransactionHandler
                                " did not call through to super.onConfigurationChanged()");
            }
        }
        mConfigurationChangedListenerController
                .dispatchOnConfigurationChanged(activity.getActivityToken());

        return configToReport;
    }
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 android.app;

import android.annotation.NonNull;
import android.os.IBinder;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Manages listeners for unfiltered configuration changes.
 * @hide
 */
class ConfigurationChangedListenerController {

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final List<ListenerContainer> mListenerContainers = new ArrayList<>();

    /**
     * Adds a listener to receive updates when they are dispatched. This only dispatches updates and
     * does not relay the last emitted value. If called with the same listener then this method does
     * not have any effect.
     * @param executor an executor that is used to dispatch the updates.
     * @param consumer a listener interested in receiving updates.
     */
    void addListener(@NonNull Executor executor,
            @NonNull Consumer<IBinder> consumer) {
        synchronized (mLock) {
            if (indexOf(consumer) > -1) {
                return;
            }
            mListenerContainers.add(new ListenerContainer(executor, consumer));
        }
    }

    /**
     * Removes the listener that was previously registered. If the listener was not registered this
     * method does not have any effect.
     */
    void removeListener(@NonNull Consumer<IBinder> consumer) {
        synchronized (mLock) {
            final int index = indexOf(consumer);
            if (index > -1) {
                mListenerContainers.remove(index);
            }
        }
    }

    /**
     * Dispatches the update to all registered listeners
     * @param activityToken a token for the {@link Activity} that received a configuration update.
     */
    void dispatchOnConfigurationChanged(@NonNull IBinder activityToken) {
        final List<ListenerContainer> consumers;
        synchronized (mLock) {
            consumers = new ArrayList<>(mListenerContainers);
        }
        for (int i = 0; i < consumers.size(); i++) {
            consumers.get(i).accept(activityToken);
        }
    }

    @GuardedBy("mLock")
    private int indexOf(Consumer<IBinder> consumer) {
        for (int i = 0; i < mListenerContainers.size(); i++) {
            if (mListenerContainers.get(i).isMatch(consumer)) {
                return i;
            }
        }
        return -1;
    }

    private static final class ListenerContainer {

        @NonNull
        private final Executor mExecutor;
        @NonNull
        private final Consumer<IBinder> mConsumer;

        ListenerContainer(@NonNull Executor executor,
                @NonNull Consumer<IBinder> consumer) {
            mExecutor = executor;
            mConsumer = consumer;
        }

        public boolean isMatch(@NonNull Consumer<IBinder> consumer) {
            return mConsumer.equals(consumer);
        }

        public void accept(@NonNull IBinder activityToken) {
            mExecutor.execute(() -> mConsumer.accept(activityToken));
        }

    }
}
+35 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;

import android.app.Activity;
import android.app.ActivityThread;
import android.app.Application;
import android.app.WindowConfiguration;
import android.content.ComponentCallbacks;
@@ -33,6 +34,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Log;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -59,7 +61,7 @@ import java.util.Set;
 * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
 */
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    private static final String TAG = "SampleExtension";
    private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName();

    private final Object mLock = new Object();

@@ -81,6 +83,9 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
            mJavaToExtConsumers = new ArrayMap<>();

    private final RawConfigurationChangedListener mRawConfigurationChangedListener =
            new RawConfigurationChangedListener();

    public WindowLayoutComponentImpl(@NonNull Context context,
            @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
        ((Application) context.getApplicationContext())
@@ -109,6 +114,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
        synchronized (mLock) {
            mJavaToExtConsumers.put(consumer, extConsumer);
            updateListenerRegistrations();
        }
        addWindowLayoutInfoListener(activity, extConsumer);
    }
@@ -162,6 +168,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        final Consumer<WindowLayoutInfo> extConsumer;
        synchronized (mLock) {
            extConsumer = mJavaToExtConsumers.remove(consumer);
            updateListenerRegistrations();
        }
        if (extConsumer != null) {
            removeWindowLayoutInfoListener(extConsumer);
@@ -191,6 +198,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        }
    }

    @GuardedBy("mLock")
    private void updateListenerRegistrations() {
        ActivityThread currentThread = ActivityThread.currentActivityThread();
        if (mJavaToExtConsumers.isEmpty()) {
            currentThread.removeConfigurationChangedListener(mRawConfigurationChangedListener);
        } else {
            currentThread.addConfigurationChangedListener(Runnable::run,
                    mRawConfigurationChangedListener);
        }
    }

    @GuardedBy("mLock")
    @NonNull
    private Set<Context> getContextsListeningForLayoutChanges() {
@@ -336,25 +354,28 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
                continue;
            }
            if (featureRect.left != 0 && featureRect.top != 0) {
                throw new IllegalArgumentException("Bounding rectangle must start at the top or "
                Log.wtf(TAG, "Bounding rectangle must start at the top or "
                        + "left of the window. BaseFeatureRect: " + baseFeature.getRect()
                        + ", FeatureRect: " + featureRect
                        + ", WindowConfiguration: " + windowConfiguration);
                continue;

            }
            if (featureRect.left == 0
                    && featureRect.width() != windowConfiguration.getBounds().width()) {
                throw new IllegalArgumentException("Horizontal FoldingFeature must have full width."
                Log.wtf(TAG, "Horizontal FoldingFeature must have full width."
                        + " BaseFeatureRect: " + baseFeature.getRect()
                        + ", FeatureRect: " + featureRect
                        + ", WindowConfiguration: " + windowConfiguration);
                continue;
            }
            if (featureRect.top == 0
                    && featureRect.height() != windowConfiguration.getBounds().height()) {
                throw new IllegalArgumentException("Vertical FoldingFeature must have full height."
                Log.wtf(TAG, "Vertical FoldingFeature must have full height."
                        + " BaseFeatureRect: " + baseFeature.getRect()
                        + ", FeatureRect: " + featureRect
                        + ", WindowConfiguration: " + windowConfiguration);
                continue;
            }
            features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
        }
@@ -407,6 +428,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        }
    }

    private final class RawConfigurationChangedListener implements
            java.util.function.Consumer<IBinder> {
        @Override
        public void accept(IBinder activityToken) {
            synchronized (mLock) {
                onDisplayFeaturesChangedIfListening(activityToken);
            }
        }
    }

    private final class ConfigurationChangeListener implements ComponentCallbacks {
        final IBinder mToken;