Files
Lawnchair/src/com/android/launcher3/util/UiThreadHelper.java
T
hyunyoungs d7a02ec7ae Fix IME hiding animation being delayed after unlocking the device
Hide keyboard with WindowInsetsController if could. In case
hideSoftInputFromWindow may ignored by input connection being finished
when the screen is off.

Bug: 233158645
Test: manual as steps:
  1.  Launch AllApps.
  2.  Tap search bar to invoke IME.
  3.  Screen off.
  4.  Screen on.
And all other keyboard hide cases such as back gesture, swipe down, etc.

Change-Id: I4154f38f94b6760a430f7510a650422020d8f59a
2022-06-03 14:19:45 -07:00

147 lines
5.9 KiB
Java

/*
* Copyright (C) 2017 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.launcher3.util;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.views.ActivityContext;
/**
* Utility class for offloading some class from UI thread
*/
public class UiThreadHelper {
private static final MainThreadInitializedObject<Handler> HANDLER =
new MainThreadInitializedObject<>(
c -> new Handler(UI_HELPER_EXECUTOR.getLooper(), new UiCallbacks(c)));
private static final int MSG_HIDE_KEYBOARD = 1;
private static final int MSG_SET_ORIENTATION = 2;
private static final int MSG_RUN_COMMAND = 3;
private static final String STATS_LOGGER_KEY = "STATS_LOGGER_KEY";
@SuppressLint("NewApi")
public static void hideKeyboardAsync(ActivityContext activityContext, IBinder token) {
View root = activityContext.getDragLayer();
if (Utilities.ATLEAST_R) {
Preconditions.assertUIThread();
// Hide keyboard with WindowInsetsController if could. In case
// hideSoftInputFromWindow may get ignored by input connection being finished
// when the screen is off.
//
// In addition, inside IMF, the keyboards are closed asynchronously that launcher no
// longer need to post to the message queue.
final WindowInsetsController wic = root.getWindowInsetsController();
WindowInsets insets = root.getRootWindowInsets();
boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
if (wic != null && isImeShown) {
// this method cannot be called cross threads
wic.hide(WindowInsets.Type.ime());
activityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
return;
} else {
// print which stack trace failed.
Log.e("Launcher", "hideKeyboard ignored.", new Exception());
// Then attempt to use the old logic.
}
}
// Since the launcher context cannot be accessed directly from callback, adding secondary
// message to log keyboard close event asynchronously.
Bundle mHideKeyboardLoggerMsg = new Bundle();
mHideKeyboardLoggerMsg.putParcelable(
STATS_LOGGER_KEY,
Message.obtain(
HANDLER.get(root.getContext()),
() -> activityContext
.getStatsLogManager()
.logger()
.log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED)
)
);
Message mHideKeyboardMsg = Message.obtain(HANDLER.get(root.getContext()), MSG_HIDE_KEYBOARD,
token);
mHideKeyboardMsg.setData(mHideKeyboardLoggerMsg);
mHideKeyboardMsg.sendToTarget();
}
public static void setOrientationAsync(Activity activity, int orientation) {
Message.obtain(HANDLER.get(activity), MSG_SET_ORIENTATION, orientation, 0, activity)
.sendToTarget();
}
public static void setBackButtonAlphaAsync(Context context, AsyncCommand command, float alpha,
boolean animate) {
runAsyncCommand(context, command, Float.floatToIntBits(alpha), animate ? 1 : 0);
}
public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
Message.obtain(HANDLER.get(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
}
private static class UiCallbacks implements Handler.Callback {
private final Context mContext;
private final InputMethodManager mIMM;
UiCallbacks(Context context) {
mContext = context;
mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
}
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_HIDE_KEYBOARD:
if (mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0)) {
// log keyboard close event only when keyboard is actually closed
((Message) message.getData().getParcelable(STATS_LOGGER_KEY))
.sendToTarget();
}
return true;
case MSG_SET_ORIENTATION:
((Activity) message.obj).setRequestedOrientation(message.arg1);
return true;
case MSG_RUN_COMMAND:
((AsyncCommand) message.obj).execute(mContext, message.arg1, message.arg2);
return true;
}
return false;
}
}
public interface AsyncCommand {
void execute(Context proxy, int arg1, int arg2);
}
}