script-astra/Android/Sdk/sources/android-35/android/inputmethodservice/InkWindow.java

206 lines
7.0 KiB
Java
Raw Normal View History

2025-01-20 15:15:20 +00:00
/*
* Copyright (C) 2021 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.inputmethodservice;
import static android.view.WindowManager.LayoutParams;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import android.annotation.NonNull;
import android.content.Context;
import android.os.IBinder;
import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import com.android.internal.policy.PhoneWindow;
import java.util.Objects;
/**
* Window of type {@code LayoutParams.TYPE_INPUT_METHOD_DIALOG} for drawing
* Handwriting Ink on screen.
* @hide
*/
final class InkWindow extends PhoneWindow {
private final WindowManager mWindowManager;
private boolean mIsViewAdded;
private View mInkView;
private InkVisibilityListener mInkViewVisibilityListener;
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
public InkWindow(@NonNull Context context) {
super(context);
setType(LayoutParams.TYPE_INPUT_METHOD);
final LayoutParams attrs = getAttributes();
attrs.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
attrs.setFitInsetsTypes(0);
// disable window animations.
// TODO(b/253477462): replace with API when available
attrs.windowAnimations = -1;
// TODO(b/210039666): use INPUT_FEATURE_NO_INPUT_CHANNEL once b/216179339 is fixed.
setAttributes(attrs);
// Ink window is not touchable with finger.
addFlags(FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_TOUCHABLE
| FLAG_NOT_FOCUSABLE);
setBackgroundDrawableResource(android.R.color.transparent);
setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mWindowManager = context.getSystemService(WindowManager.class);
}
/**
* Initialize InkWindow if we only want to create and draw surface but not show it.
*/
void initOnly() {
show(true /* keepInvisible */);
}
/**
* Method to show InkWindow on screen.
* Emulates internal behavior similar to Dialog.show().
*/
void show() {
show(false /* keepInvisible */);
}
private void show(boolean keepInvisible) {
if (getDecorView() == null) {
Slog.i(InputMethodService.TAG, "DecorView is not set for InkWindow. show() failed.");
return;
}
getDecorView().setVisibility(keepInvisible ? View.INVISIBLE : View.VISIBLE);
if (!mIsViewAdded) {
mWindowManager.addView(getDecorView(), getAttributes());
mIsViewAdded = true;
}
}
/**
* Method to hide InkWindow from screen.
* Emulates internal behavior similar to Dialog.hide().
* @param remove set {@code true} to remove InkWindow surface completely.
*/
void hide(boolean remove) {
if (getDecorView() != null) {
if (remove) {
mWindowManager.removeViewImmediate(getDecorView());
} else {
getDecorView().setVisibility(View.INVISIBLE);
}
}
}
void setToken(@NonNull IBinder token) {
WindowManager.LayoutParams lp = getAttributes();
lp.token = token;
setAttributes(lp);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
if (mInkView == null) {
mInkView = view;
} else if (mInkView != view) {
throw new IllegalStateException("Only one Child Inking view is permitted.");
}
super.addContentView(view, params);
initInkViewVisibilityListener();
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
mInkView = view;
super.setContentView(view, params);
initInkViewVisibilityListener();
}
@Override
public void setContentView(View view) {
mInkView = view;
super.setContentView(view);
initInkViewVisibilityListener();
}
@Override
public void clearContentView() {
if (mGlobalLayoutListener != null && mInkView != null) {
mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
}
mGlobalLayoutListener = null;
mInkView = null;
super.clearContentView();
}
/**
* Listener used by InkWindow to time the dispatching of {@link MotionEvent}s to Ink view, once
* it is visible to user.
*/
interface InkVisibilityListener {
void onInkViewVisible();
}
void setInkViewVisibilityListener(InkVisibilityListener listener) {
mInkViewVisibilityListener = listener;
initInkViewVisibilityListener();
}
void initInkViewVisibilityListener() {
if (mInkView == null || mInkViewVisibilityListener == null
|| mGlobalLayoutListener != null) {
return;
}
mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mInkView == null) {
return;
}
if (mInkView.isVisibleToUser()) {
if (mInkViewVisibilityListener != null) {
mInkViewVisibilityListener.onInkViewVisible();
}
mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mGlobalLayoutListener = null;
}
}
};
mInkView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
boolean isInkViewVisible() {
return getDecorView().getVisibility() == View.VISIBLE
&& mInkView != null && mInkView.isVisibleToUser();
}
void dispatchHandwritingEvent(@NonNull MotionEvent event) {
final View decor = getDecorView();
Objects.requireNonNull(decor);
final ViewRootImpl viewRoot = decor.getViewRootImpl();
Objects.requireNonNull(viewRoot);
// The view root will own the event that we enqueue, so provide a copy of the event.
viewRoot.enqueueInputEvent(MotionEvent.obtain(event));
}
}