Merge "Use parentUserId when reading config_imeDrawsImeNavBar" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
18654af7fd
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user