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

Commit 18654af7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use parentUserId when reading config_imeDrawsImeNavBar" into tm-dev

parents 598d987c f93769b1
Loading
Loading
Loading
Loading
+26 −57
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.inputmethodservice;

import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;

import android.animation.ValueAnimator;
@@ -24,18 +23,12 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.inputmethodservice.navigationbar.NavigationBarFrame;
import android.inputmethodservice.navigationbar.NavigationBarView;
import android.os.PatternMatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -145,9 +138,6 @@ final class NavigationBarController {
        @Nullable
        Insets mLastInsets;

        @Nullable
        private BroadcastReceiver mSystemOverlayChangedReceiver;

        private boolean mShouldShowImeSwitcherWhenImeIsShown;

        @Appearance
@@ -360,14 +350,6 @@ final class NavigationBarController {
            });
        }

        private boolean imeDrawsImeNavBar() {
            final Resources resources = mService.getResources();
            if (resources == null) {
                return false;
            }
            return resources.getBoolean(com.android.internal.R.bool.config_imeDrawsImeNavBar);
        }

        @Override
        public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
            final Window window = softInputWindow.getWindow();
@@ -380,27 +362,6 @@ final class NavigationBarController {
            if (mDestroyed) {
                return;
            }
            mImeDrawsImeNavBar = imeDrawsImeNavBar();
            if (mSystemOverlayChangedReceiver == null) {
                final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
                intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
                intentFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
                mSystemOverlayChangedReceiver = new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        if (mDestroyed) {
                            return;
                        }
                        mImeDrawsImeNavBar = imeDrawsImeNavBar();
                        if (mImeDrawsImeNavBar) {
                            installNavigationBarFrameIfNecessary();
                        } else {
                            uninstallNavigationBarFrameIfNecessary();
                        }
                    }
                };
                mService.registerReceiver(mSystemOverlayChangedReceiver, intentFilter);
            }
            installNavigationBarFrameIfNecessary();
        }

@@ -413,10 +374,6 @@ final class NavigationBarController {
                mTintAnimator.cancel();
                mTintAnimator = null;
            }
            if (mSystemOverlayChangedReceiver != null) {
                mService.unregisterReceiver(mSystemOverlayChangedReceiver);
                mSystemOverlayChangedReceiver = null;
            }
            mDestroyed = true;
        }

@@ -454,19 +411,28 @@ final class NavigationBarController {
                return;
            }

            final boolean imeDrawsImeNavBar =
                    (navButtonFlags & InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR) != 0;
            final boolean shouldShowImeSwitcherWhenImeIsShown =
                    (navButtonFlags & InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN)
                    != 0;
            if (mShouldShowImeSwitcherWhenImeIsShown == shouldShowImeSwitcherWhenImeIsShown) {
                return;
            }

            mImeDrawsImeNavBar = imeDrawsImeNavBar;
            final boolean prevShouldShowImeSwitcherWhenImeIsShown =
                    mShouldShowImeSwitcherWhenImeIsShown;
            mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;

            if (imeDrawsImeNavBar) {
                installNavigationBarFrameIfNecessary();
                if (mNavigationBarFrame == null) {
                    return;
                }
            final NavigationBarView navigationBarView =
                    mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
                if (mShouldShowImeSwitcherWhenImeIsShown
                        == prevShouldShowImeSwitcherWhenImeIsShown) {
                    return;
                }
                final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
                        NavigationBarView.class::isInstance);
                if (navigationBarView == null) {
                    return;
                }
@@ -474,6 +440,9 @@ final class NavigationBarController {
                        | (shouldShowImeSwitcherWhenImeIsShown
                                ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
                navigationBarView.setNavigationIconHints(hints);
            } else {
                uninstallNavigationBarFrameIfNecessary();
            }
        }

        @Override
+7 −1
Original line number Diff line number Diff line
@@ -33,11 +33,17 @@ import java.lang.annotation.Retention;
 */
@Retention(SOURCE)
@IntDef(flag = true, value = {
        InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR,
        InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN,
})
public @interface InputMethodNavButtonFlags {
    /**
     * When set, the IME process needs to render and handle the navigation bar buttons such as the
     * back button and the IME switcher button.
     */
    int IME_DRAWS_IME_NAV_BAR = 1;
    /**
     * When set, the IME switcher icon needs to be shown on the navigation bar.
     */
    int SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN = 1;
    int SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN = 2;
}
+43 −2
Original line number Diff line number Diff line
@@ -336,6 +336,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    @GuardedBy("ImfLock.class")
    private final HandwritingModeController mHwController;

    @GuardedBy("ImfLock.class")
    @Nullable
    private OverlayableSystemBooleanResourceWrapper mImeDrawsImeNavBarRes;

    static class SessionState {
        final ClientState client;
        final IInputMethodInvoker method;
@@ -1723,12 +1727,44 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
    }

    @GuardedBy("ImfLock.class")
    private void recreateImeDrawsImeNavBarResIfNecessary(@UserIdInt int targetUserId) {
        // Currently, com.android.internal.R.bool.config_imeDrawsImeNavBar is overlaid only for the
        // profile parent user.
        // TODO(b/221443458): See if we can make OverlayManager be aware of profile groups.
        final int profileParentUserId = mUserManagerInternal.getProfileParentId(targetUserId);
        if (mImeDrawsImeNavBarRes != null
                && mImeDrawsImeNavBarRes.getUserId() != profileParentUserId) {
            mImeDrawsImeNavBarRes.close();
            mImeDrawsImeNavBarRes = null;
        }
        if (mImeDrawsImeNavBarRes == null) {
            final Context userContext;
            if (mContext.getUserId() == profileParentUserId) {
                userContext = mContext;
            } else {
                userContext = mContext.createContextAsUser(UserHandle.of(profileParentUserId),
                        0 /* flags */);
            }
            mImeDrawsImeNavBarRes = OverlayableSystemBooleanResourceWrapper.create(userContext,
                    com.android.internal.R.bool.config_imeDrawsImeNavBar, mHandler, resource -> {
                        synchronized (ImfLock.class) {
                            if (resource == mImeDrawsImeNavBarRes) {
                                sendOnNavButtonFlagsChangedLocked();
                            }
                        }
                    });
        }
    }

    @GuardedBy("ImfLock.class")
    private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
            IInputMethodClient clientToBeReset) {
        if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
                + " currentUserId=" + mSettings.getCurrentUserId());

        recreateImeDrawsImeNavBarResIfNecessary(newUserId);

        // ContentObserver should be registered again when the user is changed
        mSettingsObserver.registerContentObserverLocked(newUserId);

@@ -1833,6 +1869,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    });
                }

                recreateImeDrawsImeNavBarResIfNecessary(currentUserId);

                mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
                mSettingsObserver.registerContentObserverLocked(currentUserId);

@@ -2941,10 +2979,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    @GuardedBy("ImfLock.class")
    @InputMethodNavButtonFlags
    private int getInputMethodNavButtonFlagsLocked() {
        final boolean canImeDrawsImeNavBar =
                mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get();
        final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
                InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
        return shouldShowImeSwitcherWhenImeIsShown
                ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0;
        return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
                | (shouldShowImeSwitcherWhenImeIsShown
                        ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
    }

    @GuardedBy("ImfLock.class")
+159 −0
Original line number Diff line number Diff line
/*
 * 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.server.inputmethod;

import static android.content.Intent.ACTION_OVERLAY_CHANGED;

import android.annotation.AnyThread;
import android.annotation.BoolRes;
import android.annotation.NonNull;
import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.PatternMatcher;
import android.util.Slog;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * A wrapper object for any boolean resource defined in {@code "android"} package, in a way that is
 * aware of per-user Runtime Resource Overlay (RRO).
 */
final class OverlayableSystemBooleanResourceWrapper implements AutoCloseable {
    private static final String TAG = "OverlayableSystemBooleanResourceWrapper";

    private static final String SYSTEM_PACKAGE_NAME = "android";

    @UserIdInt
    private final int mUserId;
    @NonNull
    private final AtomicBoolean mValueRef;
    @NonNull
    private final AtomicReference<Runnable> mCleanerRef;

    /**
     * Creates {@link OverlayableSystemBooleanResourceWrapper} for the given boolean resource ID
     * with a value change callback for the user associated with the {@link Context}.
     *
     * @param userContext The {@link Context} to be used to access the resource. This needs to be
     *                    associated with the right user because the Runtime Resource Overlay (RRO)
     *                    is per-user configuration.
     * @param boolResId The resource ID to be queried.
     * @param handler {@link Handler} to be used to dispatch {@code callback}.
     * @param callback The callback to be notified when the specified value might be updated.
     *                 The callback needs to take care of spurious wakeup. The value returned from
     *                 {@link #get()} may look to be exactly the same as the previously read value
     *                 e.g. when the value is changed from {@code false} to {@code true} to
     *                 {@code false} in a very short period of time, because {@link #get()} always
     *                 does volatile-read.
     * @return New {@link OverlayableSystemBooleanResourceWrapper}.
     */
    @NonNull
    @UserHandleAware
    static OverlayableSystemBooleanResourceWrapper create(@NonNull Context userContext,
            @BoolRes int boolResId, @NonNull Handler handler,
            @NonNull Consumer<OverlayableSystemBooleanResourceWrapper> callback) {

        // Note that we cannot fully trust this initial value due to the dead time between obtaining
        // the value here and setting up a broadcast receiver for change callback below.
        // We will refresh the value again later after setting up the change callback anyway.
        final AtomicBoolean valueRef = new AtomicBoolean(evaluate(userContext, boolResId));

        final AtomicReference<Runnable> cleanerRef = new AtomicReference<>();

        final OverlayableSystemBooleanResourceWrapper object =
                new OverlayableSystemBooleanResourceWrapper(userContext.getUserId(), valueRef,
                        cleanerRef);

        final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
        intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
        intentFilter.addDataSchemeSpecificPart(SYSTEM_PACKAGE_NAME, PatternMatcher.PATTERN_LITERAL);

        final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final boolean newValue = evaluate(userContext, boolResId);
                if (newValue != valueRef.getAndSet(newValue)) {
                    callback.accept(object);
                }
            }
        };
        userContext.registerReceiver(broadcastReceiver, intentFilter,
                null /* broadcastPermission */, handler,
                Context.RECEIVER_NOT_EXPORTED);
        cleanerRef.set(() -> userContext.unregisterReceiver(broadcastReceiver));

        // Make sure that the initial observable value is obtained after the change callback is set.
        valueRef.set(evaluate(userContext, boolResId));
        return object;
    }

    private OverlayableSystemBooleanResourceWrapper(@UserIdInt int userId,
            @NonNull AtomicBoolean valueRef, @NonNull AtomicReference<Runnable> cleanerRef) {
        mUserId = userId;
        mValueRef = valueRef;
        mCleanerRef = cleanerRef;
    }

    /**
     * @return The boolean resource value.
     */
    @AnyThread
    boolean get() {
        return mValueRef.get();
    }

    /**
     * @return The user ID associated with this resource reader.
     */
    @AnyThread
    @UserIdInt
    int getUserId() {
        return mUserId;
    }

    @AnyThread
    private static boolean evaluate(@NonNull Context context, @BoolRes int boolResId) {
        try {
            return context.getPackageManager()
                    .getResourcesForApplication(SYSTEM_PACKAGE_NAME)
                    .getBoolean(boolResId);
        } catch (PackageManager.NameNotFoundException e) {
            Slog.e(TAG, "getResourcesForApplication(\"" + SYSTEM_PACKAGE_NAME + "\") failed", e);
            return false;
        }
    }

    /**
     * Cleans up the callback.
     */
    @AnyThread
    @Override
    public void close() {
        final Runnable cleaner = mCleanerRef.getAndSet(null);
        if (cleaner != null) {
            cleaner.run();
        }
    }
}