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

This commit is contained in:
TreeHugger Robot
2022-03-08 00:45:59 +00:00
committed by Android (Google) Code Review
4 changed files with 235 additions and 60 deletions

View File

@@ -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,26 +411,38 @@ 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 (mNavigationBarFrame == null) {
return;
if (imeDrawsImeNavBar) {
installNavigationBarFrameIfNecessary();
if (mNavigationBarFrame == null) {
return;
}
if (mShouldShowImeSwitcherWhenImeIsShown
== prevShouldShowImeSwitcherWhenImeIsShown) {
return;
}
final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
NavigationBarView.class::isInstance);
if (navigationBarView == null) {
return;
}
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
| (shouldShowImeSwitcherWhenImeIsShown
? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
} else {
uninstallNavigationBarFrameIfNecessary();
}
final NavigationBarView navigationBarView =
mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
if (navigationBarView == null) {
return;
}
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
| (shouldShowImeSwitcherWhenImeIsShown
? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
}
@Override

View File

@@ -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;
}

View File

@@ -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")

View File

@@ -0,0 +1,159 @@
/*
* 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();
}
}
}